package cli import ( "coop-cloud-backend/cli/external" "coop-cloud-backend/data" "encoding/json" "log" "net/http" "net/http/httptest" "testing" appPkg "coopcloud.tech/abra/pkg/app" "github.com/stretchr/testify/assert" ) func ToAny[T any](s []T) []any { out := make([]any, len(s)) for i, v := range s { out[i] = v } return out } // The mock call flow is // h.cmd.Run -> h.ap.getApps -> h.ap.GetReachableServers -> h.ap.GetAppStatuses // -(for each reachable app)> h.ap.StackName(app) // -(reachable apps with version != "unknown" && chaos == "false")> h.rp.EnsureExists(app) and h.rp.Tags(app) func TestListApps(t *testing.T) { tests := []struct { name string // these are all of our "external" package function calls getAppsArgs []string getAppsOut []appPkg.App getAppsErr error getServsArgs []appPkg.App getServsOut map[string]bool getServsErr error getStatusesArgs []interface{} getStatusesOut map[string]map[string]string getStatusesErr error stackNameArgs []appPkg.App stackNameOut []string // these are arrays for all apps ensureArgs []appPkg.App ensureErrs []error tagsArgs []appPkg.App tagsOuts [][]string tagsErrs []error // these are the details for h.cmd.Run's output. Luckily the input isalways fixed reqBody string runOutput []byte runErr error // this is the final information i guess, but i need the final json thingy too expectedBody ServerAppsResponse expectedCode int }{ { name: "success one simple app", getAppsArgs: []string{"", ""}, getAppsOut: []appPkg.App{data.App()}, getServsArgs: []appPkg.App{data.App()}, getServsOut: map[string]bool{"example.com": true}, getStatusesArgs: []interface{}{data.App(), true}, getStatusesOut: data.AppStatus(), stackNameArgs: []appPkg.App{data.App()}, stackNameOut: []string{"nextcloud"}, ensureArgs: []appPkg.App{data.App()}, ensureErrs: []error{nil}, tagsArgs: []appPkg.App{data.App()}, tagsOuts: [][]string{{"1.2.0"}}, tagsErrs: []error{nil}, runOutput: []byte(` { "example.com": { "apps": [{ "server": "example.com", "recipe": "nextcloud", "appName": "nextcloud.example.com", "domain": "nextcloud.example.com", "status": "", "chaos": "", "chaosVersion": "", "version": "", "upgrade": "" }] } } `), expectedCode: http.StatusOK, expectedBody: ServerAppsResponse{ "example.com": { Apps: []AbraApp{AbraApp{ Server: "example.com", Recipe: "nextcloud", AppName: "nextcloud.example.com", Domain: "nextcloud.example.com", Status: "running", Chaos: "false", ChaosVersion: "unknown", Version: "1.0.0", Upgrade: "1.2.0", }}, AppCount: 1, VersionCount: 1, UnversionedCount: 0, LatestCount: 0, UpgradeCount: 1, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockCmd := new(MockCommandRunner) mockAppPkg := new(external.MockAppPackage) mockServerPkg := new(external.MockServerPackage) mockRecipePkg := new(external.MockRecipePackage) mockCmd. On("Run", "abra", "app", "ls", "-m"). Return(tt.runOutput, tt.runErr) mockAppPkg. On("GetApps", ToAny(tt.getAppsArgs)...). Return(tt.getAppsOut, tt.getAppsErr) mockServerPkg. On("GetReachableServers", ToAny(tt.getServsArgs)...). Return(tt.getServsOut, tt.getServsErr) mockAppPkg. On("GetAppStatuses", tt.getStatusesArgs...). Return(tt.getStatusesOut, tt.getStatusesErr) for i := range tt.stackNameArgs { mockAppPkg. On("StackName", []interface{}{tt.stackNameArgs[i]}...). Return(tt.stackNameOut[i]) } for i, ensureArg := range tt.ensureArgs { mockRecipePkg. On("EnsureExists", ensureArg). Return(tt.ensureErrs[i]) log.Printf("tag output %v", tt.tagsOuts[i]) mockRecipePkg. On("Tags", tt.tagsArgs[i]). Return(tt.tagsOuts[i], tt.tagsErrs[i]) } h := &abraHandler{cmd: mockCmd, ap: mockAppPkg, sp: mockServerPkg, rp: mockRecipePkg} req := httptest.NewRequest(http.MethodPost, "/", nil) rec := httptest.NewRecorder() h.handleListApps(rec, req) assert.Equal(t, tt.expectedCode, rec.Code) var got ServerAppsResponse if assert.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got)) { assert.Equal(t, tt.expectedBody, got) } // may need to have "expectedRun" flags later mockCmd.AssertExpectations(t) mockAppPkg.AssertExpectations(t) mockServerPkg.AssertExpectations(t) mockRecipePkg.AssertExpectations(t) }) } }