package members_client import ( "os" "path/filepath" "strings" "testing" "code.tnxs.net/vernonkeenan/lib/api/members/members_client/track_topics" "code.tnxs.net/vernonkeenan/lib/api/members/members_client/track_users" "code.tnxs.net/vernonkeenan/lib/api/members/members_models" "github.com/go-openapi/strfmt" ) func TestTrackTopicClientExposesOnlyReadAndImmutableCreate(t *testing.T) { client := track_topics.New(nil, strfmt.Default) if client == nil { t.Fatal("generated TrackTopic client is nil") } var _ track_topics.ClientService = client params := track_topics.NewGetTrackTopicsParams(). WithID(trackRelationshipString("association")). WithTrackID(trackRelationshipString("track")). WithTopicID(trackRelationshipString("topic")) if params.ID == nil || *params.ID != "association" || params.TrackID == nil || *params.TrackID != "track" || params.TopicID == nil || *params.TopicID != "topic" { t.Fatal("generated TrackTopic GET filters do not preserve values") } request := &members_models.TrackTopicRequest{Data: []*members_models.TrackTopic{{ TopicID: "topic", TrackID: "track", }}} create := track_topics.NewPostTrackTopicsParams().WithTrackTopicRequest(request) if create.TrackTopicRequest != request { t.Fatal("generated TrackTopic POST does not preserve its request") } assertNoGeneratedOperation(t, "track_topics", "put_", "delete_") } func TestTrackUserClientExposesReadCreateAndRoleOnlyUpdate(t *testing.T) { client := track_users.New(nil, strfmt.Default) if client == nil { t.Fatal("generated TrackUser client is nil") } var _ track_users.ClientService = client params := track_users.NewGetTrackUsersParams(). WithID(trackRelationshipString("association")). WithTrackID(trackRelationshipString("track")). WithUserID(trackRelationshipString("user")). WithRole(trackRelationshipString(members_models.TrackUserRoleModerator)) if params.ID == nil || *params.ID != "association" || params.TrackID == nil || *params.TrackID != "track" || params.UserID == nil || *params.UserID != "user" || params.Role == nil || *params.Role != "Moderator" { t.Fatal("generated TrackUser GET filters do not preserve values") } request := &members_models.TrackUserRequest{Data: []*members_models.TrackUser{{ ID: "association", LastModifiedDate: trackRelationshipString("version"), Role: members_models.TrackUserRolePresenter, }}} create := track_users.NewPostTrackUsersParams().WithTrackUserRequest(request) update := track_users.NewPutTrackUsersParams().WithTrackUserRequest(request) if create.TrackUserRequest != request || update.TrackUserRequest != request { t.Fatal("generated TrackUser mutations do not preserve their request") } if members_models.TrackUserRoleAttendee != "Attendee" || members_models.TrackUserRoleModerator != "Moderator" || members_models.TrackUserRolePresenter != "Presenter" { t.Fatal("generated TrackUser role vocabulary changed") } assertNoGeneratedOperation(t, "track_users", "delete_") } func TestMembersClientWiresTrackRelationshipServices(t *testing.T) { client := New(nil, strfmt.Default) if client.TrackTopics == nil || client.TrackUsers == nil { t.Fatal("Members root client does not wire Track relationship services") } } func TestTrackRelationshipSpecPreservesExactScopesAndCompoundAuth(t *testing.T) { raw, err := os.ReadFile("../../../swagger/members-vernonkeenan.yaml") if err != nil { t.Fatal(err) } spec := string(raw) tests := []struct { path, method, operationID, scope string }{ {"/tracktopics", "get", "getTrackTopics", "members:track-topic:read"}, {"/tracktopics", "post", "postTrackTopics", "members:track-topic:create"}, {"/trackusers", "get", "getTrackUsers", "members:track-user:read"}, {"/trackusers", "post", "postTrackUsers", "members:track-user:create"}, {"/trackusers", "put", "putTrackUsers", "members:track-user:update"}, } const compoundSecurity = " security:\n - ApiKeyAuth: []\n kvSessionCookie: []" for _, test := range tests { block := trackRelationshipOperationBlock(t, spec, test.path, test.method) if !strings.Contains(block, " operationId: "+test.operationID+"\n") { t.Errorf("%s %s lost operationId %q", strings.ToUpper(test.method), test.path, test.operationID) } if strings.Count(block, compoundSecurity) != 1 { t.Errorf("%s %s does not preserve compound authentication", strings.ToUpper(test.method), test.path) } if !strings.Contains(block, test.scope) { t.Errorf("%s %s lost exact scope %q", strings.ToUpper(test.method), test.path, test.scope) } } if strings.Contains(trackRelationshipPathBlock(t, spec, "/tracktopics"), " put:") || strings.Contains(trackRelationshipPathBlock(t, spec, "/tracktopics"), " delete:") { t.Fatal("TrackTopic spec exposes unsupported mutation") } if strings.Contains(trackRelationshipPathBlock(t, spec, "/trackusers"), " delete:") { t.Fatal("TrackUser spec exposes unsupported delete") } if !track_users.NewPutTrackUsersConflict().IsCode(409) { t.Fatal("TrackUser update client lost the optimistic concurrency response") } } func TestTrackRelationshipClientHasNoLegacyOrCredentialSurface(t *testing.T) { for _, root := range []string{ "track_topics", "track_users", "../members_models", } { err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() || (root == "../members_models" && !strings.Contains(path, "track_topic") && !strings.Contains(path, "track_user")) { return nil } raw, readErr := os.ReadFile(path) if readErr != nil { return readErr } source := strings.ToLower(string(raw)) forbiddenValues := []string{ "salesforce", "sf-gate", "go-force", "cache", } if root == "../members_models" { forbiddenValues = append( forbiddenValues, "api_key", "credential", "password", "token", ) } for _, forbidden := range forbiddenValues { if strings.Contains(source, forbidden) { t.Errorf("%s contains forbidden coupling %q", path, forbidden) } } return nil }) if err != nil { t.Fatal(err) } } } func assertNoGeneratedOperation(t *testing.T, directory string, prefixes ...string) { t.Helper() entries, err := os.ReadDir(directory) if err != nil { t.Fatal(err) } for _, entry := range entries { name := strings.ToLower(entry.Name()) for _, prefix := range prefixes { if strings.HasPrefix(name, prefix) { t.Errorf("forbidden operation artifact: %s/%s", directory, name) } } } } func trackRelationshipString(value string) *string { return &value } func trackRelationshipPathBlock(t *testing.T, spec, path string) string { t.Helper() startMarker := "\n " + path + ":\n" start := strings.Index(spec, startMarker) if start < 0 { t.Fatalf("missing path %s", path) } block := spec[start+1:] if end := strings.Index(block, "\n /"); end >= 0 { block = block[:end] } return block } func trackRelationshipOperationBlock(t *testing.T, spec, path, method string) string { t.Helper() pathBlock := trackRelationshipPathBlock(t, spec, path) startMarker := "\n " + method + ":\n" start := strings.Index("\n"+pathBlock, startMarker) if start < 0 { t.Fatalf("missing %s operation for %s", method, path) } block := ("\n" + pathBlock)[start+1:] for _, next := range []string{"get", "post", "put", "patch", "delete"} { if next == method { continue } if end := strings.Index(block, "\n "+next+":\n"); end >= 0 { block = block[:end] } } return block }