package deploy_test import ( "context" "database/sql" "log/slog" "net/http" "net/http/httptest" "os" "strings" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/fx/fxtest" "sneak.berlin/go/upaas/internal/config" "sneak.berlin/go/upaas/internal/database" "sneak.berlin/go/upaas/internal/docker" "sneak.berlin/go/upaas/internal/logger" "sneak.berlin/go/upaas/internal/models" "sneak.berlin/go/upaas/internal/service/deploy" ) // TestRecordDeployedImageRemovesOldImages runs the step after a deploy // against a fake Docker API. Image one is also tagged for another app, // image two was the previous image, three the current one, four is new. func TestRecordDeployedImageRemovesOldImages(t *testing.T) { t.Parallel() var ( mu sync.Mutex removed []string forced bool ) srv := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") switch { case r.Method == http.MethodDelete: _, name, _ := strings.Cut(r.URL.Path, "/images/") mu.Lock() removed = append(removed, name) forced = forced || r.URL.Query().Get("force") != "" mu.Unlock() _, _ = w.Write([]byte(`[]`)) case strings.HasSuffix(r.URL.Path, "/images/json"): _, _ = w.Write([]byte(`[ {"Id":"sha256:one","RepoTags":["upaas-myapp:1","upaas-otherapp:7"]}, {"Id":"sha256:two","RepoTags":["upaas-myapp:2"]}, {"Id":"sha256:three","RepoTags":["upaas-myapp:3"]}, {"Id":"sha256:four","RepoTags":["upaas-myapp:4"]} ]`)) default: _, _ = w.Write([]byte(`{}`)) } }, )) t.Cleanup(srv.Close) log := slog.New(slog.NewTextHandler(os.Stderr, nil)) lifecycle := fxtest.NewLifecycle(t) dockerClient, err := docker.New(lifecycle, docker.Params{ Logger: logger.NewForTest(log), Config: &config.Config{DockerHost: "tcp://" + srv.Listener.Addr().String()}, }) require.NoError(t, err) lifecycle.RequireStart() t.Cleanup(lifecycle.RequireStop) db := database.NewTestDatabase(t) ctx := context.Background() app := models.NewApp(db) app.ID = "myapp-id" app.Name = "myapp" app.ImageID = sql.NullString{String: "sha256:three", Valid: true} app.PreviousImageID = sql.NullString{String: "sha256:two", Valid: true} require.NoError(t, app.Save(ctx)) deployment := models.NewDeployment(db) deployment.AppID = app.ID require.NoError(t, deployment.Save(ctx)) svc := deploy.NewTestServiceWithConfig(log, &config.Config{}, dockerClient) err = svc.RecordDeployedImage(ctx, app, deployment, "sha256:four") require.NoError(t, err) assert.Equal(t, "sha256:four", app.ImageID.String) assert.Equal(t, "sha256:three", app.PreviousImageID.String) mu.Lock() defer mu.Unlock() assert.Equal(t, []string{"upaas-myapp:1", "upaas-myapp:2"}, removed) assert.False(t, forced, "old image tags must be removed without force") }