gogcli/internal/googleauth/service_test.go

420 lines
10 KiB
Go

package googleauth
import "testing"
func TestParseService(t *testing.T) {
tests := []struct {
in string
want Service
}{
{"gmail", ServiceGmail},
{"GMAIL", ServiceGmail},
{"calendar", ServiceCalendar},
{"drive", ServiceDrive},
{"docs", ServiceDocs},
{"contacts", ServiceContacts},
{"tasks", ServiceTasks},
{"people", ServicePeople},
{"sheets", ServiceSheets},
{"groups", ServiceGroups},
{"keep", ServiceKeep},
}
for _, tt := range tests {
got, err := ParseService(tt.in)
if err != nil {
t.Fatalf("ParseService(%q) err: %v", tt.in, err)
}
if got != tt.want {
t.Fatalf("ParseService(%q) = %q, want %q", tt.in, got, tt.want)
}
}
}
func TestParseService_Invalid(t *testing.T) {
if _, err := ParseService("nope"); err == nil {
t.Fatalf("expected error")
}
}
func TestExtractCodeAndState(t *testing.T) {
code, state, err := extractCodeAndState("http://localhost:1/?code=abc&state=xyz")
if err != nil {
t.Fatalf("err: %v", err)
}
if code != "abc" || state != "xyz" {
t.Fatalf("unexpected: code=%q state=%q", code, state)
}
}
func TestExtractCodeAndState_Errors(t *testing.T) {
if _, _, err := extractCodeAndState("not a url"); err == nil {
t.Fatalf("expected error")
}
if _, _, err := extractCodeAndState("http://localhost:1/?state=xyz"); err == nil {
t.Fatalf("expected error")
}
}
func TestAllServices(t *testing.T) {
svcs := AllServices()
if len(svcs) != 10 {
t.Fatalf("unexpected: %v", svcs)
}
seen := make(map[Service]bool)
for _, s := range svcs {
seen[s] = true
}
for _, want := range []Service{ServiceGmail, ServiceCalendar, ServiceDrive, ServiceDocs, ServiceContacts, ServiceTasks, ServicePeople, ServiceSheets, ServiceGroups, ServiceKeep} {
if !seen[want] {
t.Fatalf("missing %q", want)
}
}
}
func TestUserServices(t *testing.T) {
svcs := UserServices()
if len(svcs) != 8 {
t.Fatalf("unexpected: %v", svcs)
}
seenDocs := false
for _, s := range svcs {
switch s {
case ServiceDocs:
seenDocs = true
case ServiceKeep:
t.Fatalf("unexpected keep in user services")
}
}
if !seenDocs {
t.Fatalf("missing docs in user services")
}
}
func TestUserServiceCSV(t *testing.T) {
want := "gmail,calendar,drive,docs,contacts,tasks,sheets,people"
if got := UserServiceCSV(); got != want {
t.Fatalf("unexpected user services csv: %q", got)
}
}
func TestServiceOrderCoverage(t *testing.T) {
seen := make(map[Service]bool)
for _, svc := range serviceOrder {
seen[svc] = true
if _, ok := serviceInfoByService[svc]; !ok {
t.Fatalf("missing info for %q", svc)
}
}
for svc := range serviceInfoByService {
if !seen[svc] {
t.Fatalf("service %q missing from order", svc)
}
}
}
func TestServicesInfo_Metadata(t *testing.T) {
infos := ServicesInfo()
if len(infos) != len(serviceOrder) {
t.Fatalf("unexpected services info length: %d", len(infos))
}
docsInfo, foundDocs := findServiceInfo(infos, ServiceDocs)
if !foundDocs {
t.Fatalf("missing docs info")
}
if len(docsInfo.APIs) == 0 {
t.Fatalf("docs APIs missing")
}
for _, want := range []string{
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/documents",
} {
if !containsScope(docsInfo.Scopes, want) {
t.Fatalf("docs missing scope %q", want)
}
}
if markdown := ServicesMarkdown(infos); markdown == "" {
t.Fatalf("expected markdown output")
}
}
func findServiceInfo(infos []ServiceInfo, svc Service) (ServiceInfo, bool) {
for _, info := range infos {
if info.Service == svc {
return info, true
}
}
return ServiceInfo{}, false
}
func containsScope(scopes []string, want string) bool {
for _, scope := range scopes {
if scope == want {
return true
}
}
return false
}
func TestScopesForServices_UnionSorted(t *testing.T) {
scopes, err := ScopesForServices([]Service{ServiceContacts, ServiceGmail, ServiceTasks, ServicePeople, ServiceContacts})
if err != nil {
t.Fatalf("err: %v", err)
}
if len(scopes) < 3 {
t.Fatalf("unexpected scopes: %v", scopes)
}
// Ensure stable sorting.
for i := 1; i < len(scopes); i++ {
if scopes[i-1] > scopes[i] {
t.Fatalf("not sorted: %v", scopes)
}
}
// Ensure expected scopes are included.
want := []string{
"https://mail.google.com/",
"https://www.googleapis.com/auth/contacts",
"https://www.googleapis.com/auth/contacts.other.readonly",
"https://www.googleapis.com/auth/directory.readonly",
"https://www.googleapis.com/auth/tasks",
"profile",
}
for _, w := range want {
found := false
for _, s := range scopes {
if s == w {
found = true
break
}
}
if !found {
t.Fatalf("missing scope %q in %v", w, scopes)
}
}
}
func TestScopesForManageWithOptions_Readonly(t *testing.T) {
scopes, err := ScopesForManageWithOptions([]Service{ServiceGmail, ServiceDrive, ServiceCalendar, ServiceContacts, ServiceTasks, ServiceSheets, ServiceDocs, ServicePeople}, ScopeOptions{
Readonly: true,
DriveScope: DriveScopeFull,
})
if err != nil {
t.Fatalf("err: %v", err)
}
want := []string{
scopeOpenID,
scopeEmail,
scopeUserinfoEmail,
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/drive.readonly",
"https://www.googleapis.com/auth/calendar.readonly",
"https://www.googleapis.com/auth/contacts.readonly",
"https://www.googleapis.com/auth/tasks.readonly",
"https://www.googleapis.com/auth/spreadsheets.readonly",
"https://www.googleapis.com/auth/documents.readonly",
"profile",
}
for _, w := range want {
if !containsScope(scopes, w) {
t.Fatalf("missing %q in %v", w, scopes)
}
}
notWant := []string{
"https://mail.google.com/",
"https://www.googleapis.com/auth/gmail.settings.basic",
"https://www.googleapis.com/auth/gmail.settings.sharing",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/contacts",
"https://www.googleapis.com/auth/tasks",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/documents",
}
for _, nw := range notWant {
if containsScope(scopes, nw) {
t.Fatalf("unexpected %q in %v", nw, scopes)
}
}
}
func TestScopes_ServiceKeep_DefaultIsReadonly(t *testing.T) {
scopes, err := Scopes(ServiceKeep)
if err != nil {
t.Fatalf("Scopes: %v", err)
}
if len(scopes) != 1 || scopes[0] != "https://www.googleapis.com/auth/keep.readonly" {
t.Fatalf("unexpected keep scopes: %#v", scopes)
}
}
func TestScopesForServiceWithOptions_ServiceKeep_Readonly(t *testing.T) {
scopes, err := scopesForServiceWithOptions(ServiceKeep, ScopeOptions{Readonly: true})
if err != nil {
t.Fatalf("scopesForServiceWithOptions: %v", err)
}
if len(scopes) != 1 || scopes[0] != "https://www.googleapis.com/auth/keep.readonly" {
t.Fatalf("unexpected keep readonly scopes: %#v", scopes)
}
}
func TestScopesForManageWithOptions_DriveScopeFile(t *testing.T) {
scopes, err := ScopesForManageWithOptions([]Service{ServiceDrive, ServiceDocs}, ScopeOptions{
DriveScope: DriveScopeFile,
})
if err != nil {
t.Fatalf("err: %v", err)
}
if !containsScope(scopes, "https://www.googleapis.com/auth/drive.file") {
t.Fatalf("missing drive.file in %v", scopes)
}
if containsScope(scopes, "https://www.googleapis.com/auth/drive") {
t.Fatalf("unexpected drive in %v", scopes)
}
if !containsScope(scopes, "https://www.googleapis.com/auth/documents") {
t.Fatalf("missing documents scope in %v", scopes)
}
}
func TestScopesForManageWithOptions_InvalidDriveScope(t *testing.T) {
if _, err := ScopesForManageWithOptions([]Service{ServiceDrive}, ScopeOptions{DriveScope: DriveScopeMode("nope")}); err == nil {
t.Fatalf("expected error")
}
}
func TestScopesForManageWithOptions_SheetsReadonlyIncludesDriveReadonly(t *testing.T) {
scopes, err := ScopesForManageWithOptions([]Service{ServiceSheets}, ScopeOptions{Readonly: true})
if err != nil {
t.Fatalf("err: %v", err)
}
if !containsScope(scopes, "https://www.googleapis.com/auth/spreadsheets.readonly") {
t.Fatalf("missing spreadsheets.readonly in %v", scopes)
}
if !containsScope(scopes, "https://www.googleapis.com/auth/drive.readonly") {
t.Fatalf("missing drive.readonly in %v", scopes)
}
}
func TestScopesForManageWithOptions_SheetsHonorsDriveScopeMode(t *testing.T) {
tests := []struct {
name string
opts ScopeOptions
wantDrive string
wantSheet string
}{
{
name: "default",
opts: ScopeOptions{},
wantDrive: "https://www.googleapis.com/auth/drive",
wantSheet: "https://www.googleapis.com/auth/spreadsheets",
},
{
name: "drive_readonly",
opts: ScopeOptions{DriveScope: DriveScopeReadonly},
wantDrive: "https://www.googleapis.com/auth/drive.readonly",
wantSheet: "https://www.googleapis.com/auth/spreadsheets",
},
{
name: "drive_file",
opts: ScopeOptions{DriveScope: DriveScopeFile},
wantDrive: "https://www.googleapis.com/auth/drive.file",
wantSheet: "https://www.googleapis.com/auth/spreadsheets",
},
{
name: "readonly",
opts: ScopeOptions{Readonly: true, DriveScope: DriveScopeFull},
wantDrive: "https://www.googleapis.com/auth/drive.readonly",
wantSheet: "https://www.googleapis.com/auth/spreadsheets.readonly",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
scopes, err := ScopesForManageWithOptions([]Service{ServiceSheets}, tc.opts)
if err != nil {
t.Fatalf("err: %v", err)
}
if !containsScope(scopes, tc.wantDrive) {
t.Fatalf("missing %q in %v", tc.wantDrive, scopes)
}
if !containsScope(scopes, tc.wantSheet) {
t.Fatalf("missing %q in %v", tc.wantSheet, scopes)
}
})
}
}
func TestScopes_DocsIncludesDriveAndDocsScopes(t *testing.T) {
scopes, err := Scopes(ServiceDocs)
if err != nil {
t.Fatalf("err: %v", err)
}
for _, want := range []string{
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/documents",
} {
found := false
for _, scope := range scopes {
if scope == want {
found = true
break
}
}
if !found {
t.Fatalf("missing %q in %v", want, scopes)
}
}
}
func TestScopes_GmailIncludesSettingsSharing(t *testing.T) {
scopes, err := Scopes(ServiceGmail)
if err != nil {
t.Fatalf("err: %v", err)
}
for _, want := range []string{
"https://mail.google.com/",
"https://www.googleapis.com/auth/gmail.settings.basic",
"https://www.googleapis.com/auth/gmail.settings.sharing",
} {
if !containsScope(scopes, want) {
t.Fatalf("missing %q in %v", want, scopes)
}
}
}
func TestScopes_UnknownService(t *testing.T) {
if _, err := Scopes(Service("nope")); err == nil {
t.Fatalf("expected error")
}
}