Skip to content

Commit 5483ee2

Browse files
fix(classroom): add leave confirmation and upgrade tests
- Add confirmDestructive() to leave command for consistency with other destructive operations - Add SYNC comments to shared CSS in HTML templates - Add tests for /auth/upgrade endpoint (success, missing email, creds error) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 7fdb8ea commit 5483ee2

4 files changed

Lines changed: 104 additions & 0 deletions

File tree

internal/cmd/classroom_courses.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,11 @@ func (c *ClassroomCoursesLeaveCmd) Run(ctx context.Context, flags *RootFlags) er
466466
return usage("empty user")
467467
}
468468

469+
err = confirmDestructive(ctx, flags, fmt.Sprintf("remove %s %s from course %s", role, userID, courseID))
470+
if err != nil {
471+
return err
472+
}
473+
469474
svc, err := newClassroomService(ctx, account)
470475
if err != nil {
471476
return wrapClassroomError(err)

internal/googleauth/accounts_server_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,3 +859,98 @@ func TestStartManageServer_Timeout(t *testing.T) {
859859
t.Fatalf("expected browser URL, got %q", opened)
860860
}
861861
}
862+
863+
func TestManageServer_HandleAuthUpgrade(t *testing.T) {
864+
origRead := readClientCredentials
865+
origState := randomStateFn
866+
origEndpoint := oauthEndpoint
867+
868+
t.Cleanup(func() {
869+
readClientCredentials = origRead
870+
randomStateFn = origState
871+
oauthEndpoint = origEndpoint
872+
})
873+
874+
readClientCredentials = func() (config.ClientCredentials, error) {
875+
return config.ClientCredentials{ClientID: "id", ClientSecret: "secret"}, nil
876+
}
877+
randomStateFn = func() (string, error) { return "state456", nil }
878+
oauthEndpoint = oauth2.Endpoint{AuthURL: "http://example.com/auth", TokenURL: "http://example.com/token"}
879+
880+
ln, err := (&net.ListenConfig{}).Listen(context.Background(), "tcp", "127.0.0.1:0")
881+
if err != nil {
882+
t.Fatalf("listen: %v", err)
883+
}
884+
885+
t.Cleanup(func() { _ = ln.Close() })
886+
887+
ms := &ManageServer{listener: ln}
888+
rr := httptest.NewRecorder()
889+
req := httptest.NewRequest(http.MethodGet, "/auth/upgrade?email=test@example.com", nil)
890+
ms.handleAuthUpgrade(rr, req)
891+
892+
if rr.Code != http.StatusFound {
893+
t.Fatalf("status: %d", rr.Code)
894+
}
895+
896+
loc := rr.Header().Get("Location")
897+
var parsed *url.URL
898+
899+
if p, err := url.Parse(loc); err != nil {
900+
t.Fatalf("parse location: %v", err)
901+
} else {
902+
parsed = p
903+
}
904+
905+
if parsed.Host != "example.com" {
906+
t.Fatalf("unexpected host: %q", parsed.Host)
907+
}
908+
909+
if state := parsed.Query().Get("state"); state != "state456" {
910+
t.Fatalf("unexpected state: %q", state)
911+
}
912+
913+
if ms.oauthState != "state456" {
914+
t.Fatalf("expected oauthState set")
915+
}
916+
917+
// Check for login_hint (pre-selects the email)
918+
if loginHint := parsed.Query().Get("login_hint"); loginHint != "test@example.com" {
919+
t.Fatalf("expected login_hint=test@example.com, got %q", loginHint)
920+
}
921+
922+
// Check for prompt=consent (forces consent screen)
923+
if prompt := parsed.Query().Get("prompt"); prompt != "consent" {
924+
t.Fatalf("expected prompt=consent, got %q", prompt)
925+
}
926+
}
927+
928+
func TestManageServer_HandleAuthUpgrade_MissingEmail(t *testing.T) {
929+
ms := &ManageServer{}
930+
rr := httptest.NewRecorder()
931+
req := httptest.NewRequest(http.MethodGet, "/auth/upgrade", nil)
932+
ms.handleAuthUpgrade(rr, req)
933+
934+
if rr.Code != http.StatusBadRequest {
935+
t.Fatalf("expected 400, got %d", rr.Code)
936+
}
937+
}
938+
939+
func TestManageServer_HandleAuthUpgrade_CredentialsError(t *testing.T) {
940+
origRead := readClientCredentials
941+
942+
t.Cleanup(func() { readClientCredentials = origRead })
943+
944+
readClientCredentials = func() (config.ClientCredentials, error) {
945+
return config.ClientCredentials{}, errBoom
946+
}
947+
948+
rr := httptest.NewRecorder()
949+
req := httptest.NewRequest(http.MethodGet, "/auth/upgrade?email=test@example.com", nil)
950+
ms := &ManageServer{}
951+
ms.handleAuthUpgrade(rr, req)
952+
953+
if rr.Code != http.StatusInternalServerError {
954+
t.Fatalf("expected 500, got %d", rr.Code)
955+
}
956+
}

internal/googleauth/templates/accounts.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@
242242
gap: 4px;
243243
}
244244

245+
/* SYNC: Service tag styles are shared between accounts.html and success.html
246+
* If modifying service icons/colors, update both files */
245247
.service-tag {
246248
font-size: 10px;
247249
font-weight: 500;

internal/googleauth/templates/success.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@
191191
margin-bottom: 20px;
192192
}
193193

194+
/* SYNC: Service tag styles are shared between accounts.html and success.html
195+
* If modifying service icons/colors, update both files */
194196
.service-tag {
195197
font-size: 11px;
196198
font-weight: 500;

0 commit comments

Comments
 (0)