mirror of https://github.com/vernonkeenan/lib
219 lines
7.4 KiB
Go
219 lines
7.4 KiB
Go
|
|
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
|
||
|
|
}
|