package members_client_test import ( "context" "os" "path/filepath" "reflect" "regexp" "strings" "testing" "code.tnxs.net/vernonkeenan/lib/api/members/members_client" "code.tnxs.net/vernonkeenan/lib/api/members/members_client/attendees" "code.tnxs.net/vernonkeenan/lib/api/members/members_client/events" "code.tnxs.net/vernonkeenan/lib/api/members/members_models" openapiruntime "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" ) var eventManagementTestAuth = openapiruntime.ClientAuthInfoWriterFunc( func(openapiruntime.ClientRequest, strfmt.Registry) error { return nil }, ) type eventManagementCaptureTransport struct { operation *openapiruntime.ClientOperation } func (transport *eventManagementCaptureTransport) Submit( operation *openapiruntime.ClientOperation, ) (any, error) { return transport.SubmitContext(context.Background(), operation) } func (transport *eventManagementCaptureTransport) SubmitContext( _ context.Context, operation *openapiruntime.ClientOperation, ) (any, error) { transport.operation = operation switch operation.ID { case "getEventCategories": return events.NewGetEventCategoriesOK(), nil case "postEventCategories": return events.NewPostEventCategoriesOK(), nil case "putEventCategories": return events.NewPutEventCategoriesOK(), nil case "getEvents": return events.NewGetEventsOK(), nil case "postEvents": return events.NewPostEventsOK(), nil case "putEvents": return events.NewPutEventsOK(), nil case "getAttendees": return attendees.NewGetAttendeesOK(), nil case "postAttendees": return attendees.NewPostAttendeesOK(), nil case "putAttendees": return attendees.NewPutAttendeesOK(), nil default: panic("unexpected generated Event Management operation: " + operation.ID) } } func TestGeneratedEventManagementOperationContract(t *testing.T) { type operationCall func(openapiruntime.ContextualTransport) error tests := []struct { name string wantID string wantMethod string wantPath string call operationCall }{ { name: "list or get event categories", wantID: "getEventCategories", wantMethod: "GET", wantPath: "/eventcategories", call: func(transport openapiruntime.ContextualTransport) error { _, err := events.New(transport, strfmt.Default).GetEventCategories( events.NewGetEventCategoriesParams(), eventManagementTestAuth, ) return err }, }, { name: "create event category", wantID: "postEventCategories", wantMethod: "POST", wantPath: "/eventcategories", call: func(transport openapiruntime.ContextualTransport) error { _, err := events.New(transport, strfmt.Default).PostEventCategories( events.NewPostEventCategoriesParams(), eventManagementTestAuth, ) return err }, }, { name: "update event category", wantID: "putEventCategories", wantMethod: "PUT", wantPath: "/eventcategories", call: func(transport openapiruntime.ContextualTransport) error { _, err := events.New(transport, strfmt.Default).PutEventCategories( events.NewPutEventCategoriesParams(), eventManagementTestAuth, ) return err }, }, { name: "list or get events", wantID: "getEvents", wantMethod: "GET", wantPath: "/events", call: func(transport openapiruntime.ContextualTransport) error { _, err := events.New(transport, strfmt.Default).GetEvents( events.NewGetEventsParams(), eventManagementTestAuth, ) return err }, }, { name: "create event", wantID: "postEvents", wantMethod: "POST", wantPath: "/events", call: func(transport openapiruntime.ContextualTransport) error { _, err := events.New(transport, strfmt.Default).PostEvents( events.NewPostEventsParams(), eventManagementTestAuth, ) return err }, }, { name: "update event", wantID: "putEvents", wantMethod: "PUT", wantPath: "/events", call: func(transport openapiruntime.ContextualTransport) error { _, err := events.New(transport, strfmt.Default).PutEvents( events.NewPutEventsParams(), eventManagementTestAuth, ) return err }, }, { name: "list or get attendees", wantID: "getAttendees", wantMethod: "GET", wantPath: "/attendees", call: func(transport openapiruntime.ContextualTransport) error { _, err := attendees.New(transport, strfmt.Default).GetAttendees( attendees.NewGetAttendeesParams(), eventManagementTestAuth, ) return err }, }, { name: "create attendee", wantID: "postAttendees", wantMethod: "POST", wantPath: "/attendees", call: func(transport openapiruntime.ContextualTransport) error { _, err := attendees.New(transport, strfmt.Default).PostAttendees( attendees.NewPostAttendeesParams(), eventManagementTestAuth, ) return err }, }, { name: "update attendee", wantID: "putAttendees", wantMethod: "PUT", wantPath: "/attendees", call: func(transport openapiruntime.ContextualTransport) error { _, err := attendees.New(transport, strfmt.Default).PutAttendees( attendees.NewPutAttendeesParams(), eventManagementTestAuth, ) return err }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { transport := &eventManagementCaptureTransport{} if err := test.call(transport); err != nil { t.Fatalf("generated client operation failed: %v", err) } if transport.operation == nil { t.Fatal("generated client did not submit an operation") } if transport.operation.ID != test.wantID { t.Errorf("operation ID = %q, want %q", transport.operation.ID, test.wantID) } if transport.operation.Method != test.wantMethod { t.Errorf("method = %q, want %q", transport.operation.Method, test.wantMethod) } if transport.operation.PathPattern != test.wantPath { t.Errorf("path = %q, want %q", transport.operation.PathPattern, test.wantPath) } if transport.operation.AuthInfo == nil { t.Error("generated operation discarded its auth-info writer") } }) } } func TestEventManagementSpecRequiresCompoundAuthenticationAndExactPaths(t *testing.T) { specText := readMembersLearningSpec(t) operations := map[string]map[string]string{ "/eventcategories": { "get": "getEventCategories", "post": "postEventCategories", "put": "putEventCategories", }, "/events": { "get": "getEvents", "post": "postEvents", "put": "putEvents", }, "/attendees": { "get": "getAttendees", "post": "postAttendees", "put": "putAttendees", }, } const compoundSecurity = " security:\n - ApiKeyAuth: []\n kvSessionCookie: []" for path, methods := range operations { pathBlock := learningSpecPathBlock(t, specText, path) for _, forbidden := range []string{" delete:", " patch:"} { if strings.Contains(pathBlock, forbidden) { t.Errorf("%s exposes unsupported %s", path, strings.TrimSpace(forbidden)) } } for method, operationID := range methods { operation := learningSpecOperationBlock(t, specText, path, method) if !strings.Contains(operation, " operationId: "+operationID+"\n") { t.Errorf("%s %s lost operationId %q", strings.ToUpper(method), path, operationID) } if strings.Count(operation, compoundSecurity) != 1 { t.Errorf("%s %s does not preserve compound authentication", strings.ToUpper(method), path) } } } } func TestEventManagementGetByIDAndCASContracts(t *testing.T) { recordID := "record-id" filters := []struct { name string got *string }{ {name: "EventCategory", got: events.NewGetEventCategoriesParams().WithID(&recordID).ID}, {name: "Event", got: events.NewGetEventsParams().WithID(&recordID).ID}, {name: "Attendee", got: attendees.NewGetAttendeesParams().WithID(&recordID).ID}, } for _, filter := range filters { if filter.got == nil || *filter.got != recordID { t.Errorf("%s get-by-ID filter is absent", filter.name) } } conflicts := []interface{ IsCode(int) bool }{ events.NewPutEventCategoriesConflict(), events.NewPutEventsConflict(), attendees.NewPutAttendeesConflict(), } for _, conflict := range conflicts { if !conflict.IsCode(409) { t.Errorf("%T does not preserve optimistic concurrency conflict", conflict) } } } func TestEventManagementModelsAreBoundedToProviderContract(t *testing.T) { assertExactModelFields(t, members_models.EventCategory{}, map[string]string{ "CreatedByID": "*string", "CreatedDate": "*string", "Description": "*string", "ID": "string", "ImageAltText": "*string", "ImageURL": "*string", "LastModifiedByID": "*string", "LastModifiedDate": "*string", "Logo": "*string", "Name": "*string", "Slug": "*string", }) assertExactModelFields(t, members_models.Event{}, map[string]string{ "AccountID": "*string", "Capacity": "*int64", "CreatedByID": "*string", "CreatedDate": "*string", "Description": "*string", "EndDate": "*string", "EventCategory": "*members_models.EventCategory", "EventCategoryID": "*string", "ID": "string", "ImageAltText": "*string", "ImageURL": "*string", "LastModifiedByID": "*string", "LastModifiedDate": "*string", "Location": "*string", "Logo": "*string", "Online": "*bool", "OrganizerUserID": "*string", "RegURL": "*string", "Slug": "*string", "StartDate": "*string", "Title": "*string", }) assertExactModelFields(t, members_models.Attendee{}, map[string]string{ "CheckinStatus": "int64", "CreatedDate": "*string", "EventID": "*string", "ID": "string", "LastModifiedDate": "*string", "TicketID": "*string", "UserID": "*string", }) } func TestEventManagementOmitsTicketAndDeleteClients(t *testing.T) { contracts := []struct { name string typ reflect.Type }{ {name: "Events", typ: reflect.TypeOf((*events.ClientService)(nil)).Elem()}, {name: "Attendees", typ: reflect.TypeOf((*attendees.ClientService)(nil)).Elem()}, } for _, contract := range contracts { for index := 0; index < contract.typ.NumMethod(); index++ { method := contract.typ.Method(index).Name if strings.HasPrefix(method, "Delete") || strings.Contains(method, "Ticket") { t.Errorf("%s client exposes unsafe alias %s", contract.name, method) } } } root := reflect.TypeOf(members_client.Members{}) if _, exists := root.FieldByName("Tickets"); exists { t.Fatal("root Members client exposes provider-pending Tickets") } repoRoot := learningRepoRoot(t) removed := []string{ filepath.Join(repoRoot, "api", "members", "members_client", "tickets"), filepath.Join(repoRoot, "api", "members", "members_models", "ticket.go"), filepath.Join(repoRoot, "api", "members", "members_models", "ticket_request.go"), filepath.Join(repoRoot, "api", "members", "members_models", "ticket_response.go"), } for _, path := range removed { if _, err := os.Stat(path); !os.IsNotExist(err) { t.Errorf("provider-pending Ticket artifact still exists: %s", path) } } ticketPath := learningSpecPathBlock(t, readMembersLearningSpec(t), "/tickets") if !strings.Contains(ticketPath, "x-provider-state: pending") { t.Fatal("authoritative spec lost the Ticket provider-pending marker") } if strings.Contains(ticketPath, " delete:") { t.Fatal("authoritative pending Ticket path exposes delete") } } func TestGeneratedEventManagementSurfaceHasNoExternalBypassOrSecrets(t *testing.T) { repoRoot := learningRepoRoot(t) targets := []string{ filepath.Join(repoRoot, "api", "members", "members_client", "events"), filepath.Join(repoRoot, "api", "members", "members_client", "attendees"), filepath.Join(repoRoot, "api", "members", "members_models", "event.go"), filepath.Join(repoRoot, "api", "members", "members_models", "event_category.go"), filepath.Join(repoRoot, "api", "members", "members_models", "attendee.go"), } credentialLiteral := regexp.MustCompile( `(?i)(api[_-]?key|password|secret)\s*[:=]\s*["'][^"']+["']`, ) for _, target := range targets { err := filepath.WalkDir(target, func(path string, entry os.DirEntry, walkErr error) error { if walkErr != nil { return walkErr } if entry.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") { return nil } content, err := os.ReadFile(path) if err != nil { return err } lower := strings.ToLower(string(content)) for _, forbidden := range []string{ "salesforce", "sf-gate", "go-force", "private key-----", } { if strings.Contains(lower, forbidden) { t.Errorf("%s contains forbidden boundary %q", path, forbidden) } } if credentialLiteral.Match(content) { t.Errorf("%s contains a credential-shaped literal", path) } return nil }) if err != nil { t.Fatalf("scan generated Event Management target %s: %v", target, err) } } }