From 57db68a220c1cfe85cf13dc02cea93729e205c8e Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Wed, 6 May 2026 21:44:03 -0700 Subject: [PATCH] test(cli): add testscript publish e2e --- cmd/imgcli/script_test.go | 302 ++++++++++++++++++++++ cmd/imgcli/testdata/script/publish.txtar | 30 +++ go.mod | 3 +- internal/cli/build.go | 12 +- internal/cli/build_test.go | 48 +++- internal/cli/options.go | 3 + internal/integration/publish_flow_test.go | 227 ---------------- moon.yml | 5 +- 8 files changed, 397 insertions(+), 233 deletions(-) create mode 100644 cmd/imgcli/script_test.go create mode 100644 cmd/imgcli/testdata/script/publish.txtar delete mode 100644 internal/integration/publish_flow_test.go diff --git a/cmd/imgcli/script_test.go b/cmd/imgcli/script_test.go new file mode 100644 index 0000000..71d7562 --- /dev/null +++ b/cmd/imgcli/script_test.go @@ -0,0 +1,302 @@ +//go:build integration + +package main + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + "time" + + imgsrv "github.com/meigma/imgsrv/client" + imgsrvtest "github.com/meigma/imgsrv/test" + "github.com/rogpeppe/go-internal/testscript" + + "github.com/meigma/imgcli/internal/cli" + "github.com/meigma/imgcli/internal/providers/incusos" + "github.com/meigma/imgcli/internal/publish" +) + +const ( + testEnvIncusOSCDNURL = "IMGCLI_TEST_INCUSOS_CDN_URL" + testEnvFixtureInjector = "IMGCLI_TEST_FIXTURE_INJECTOR" + + testImgsrvToken = "testtok.imgcli-script" + testVersion = "2026.05.06" + + fixtureSourcePath = "/202604261712/x86_64/IncusOS_202604261712.img.gz" + fixtureSourceBody = "fixture IncusOS source image bytes\n" + fixtureArtifact = "published IncusOS artifact bytes\n" +) + +type testscriptEnvKey struct{} + +func TestMain(m *testing.M) { + testscript.Main(m, map[string]func(){ + "imgcli": runTestscriptImgcli, + }) +} + +func TestScripts(t *testing.T) { + testscript.Run(t, testscript.Params{ + Dir: "testdata/script", + Setup: setupScript, + Cmds: map[string]func(*testscript.TestScript, bool, []string){"verify-publish": verifyPublish}, + RequireExplicitExec: true, + RequireUniqueNames: true, + }) +} + +func runTestscriptImgcli() { + opts := cli.Options{ + Version: version, + IncusOSCDNBaseURL: os.Getenv(testEnvIncusOSCDNURL), + } + if os.Getenv(testEnvFixtureInjector) != "" { + opts.IncusOSImageInjector = fixtureImageInjector{} + } + + if err := cli.ExecuteContext(context.Background(), opts); err != nil { + _, _ = fmt.Fprintln(os.Stderr, err) + os.Exit(exitFailure) + } +} + +func setupScript(env *testscript.Env) error { + tb, ok := env.T().(testing.TB) + if !ok { + return errors.New("testscript environment does not expose testing.TB") + } + tb.Helper() + + imgsrvEnv := imgsrvtest.Start(tb, imgsrvtest.WithCASPromotion(), imgsrvtest.WithAPIToken(testImgsrvToken)) + cdnServer := newFixtureCDNServer(tb) + + env.Values[testscriptEnvKey{}] = imgsrvEnv + env.Setenv("TERM", "dumb") + env.Setenv("XDG_CONFIG_HOME", filepath.Join(env.WorkDir, "xdg-config")) + env.Setenv("IMGCLI_CACHE_DIR", filepath.Join(env.WorkDir, "cache")) + env.Setenv("IMGCLI_CACHE_MAX_SIZE", "0") + env.Setenv("IMGSRV_URL", imgsrvEnv.BaseURL()) + env.Setenv("IMGSRV_TOKEN", testImgsrvToken) + env.Setenv("PUBLISH_VERSION", testVersion) + env.Setenv(testEnvIncusOSCDNURL, cdnServer.URL) + env.Setenv(testEnvFixtureInjector, "1") + + return nil +} + +func newFixtureCDNServer(tb testing.TB) *httptest.Server { + tb.Helper() + + source := []byte(fixtureSourceBody) + sourceDigest := sha256Hex(source) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/index.json": + writeFixtureIndex(tb, w, sourceDigest, int64(len(source))) + case fixtureSourcePath: + _, err := w.Write(source) + if err != nil { + tb.Fatalf("write fixture source: %v", err) + } + default: + http.NotFound(w, r) + } + })) + tb.Cleanup(server.Close) + + return server +} + +func writeFixtureIndex(tb testing.TB, w http.ResponseWriter, sourceDigest string, sourceSize int64) { + tb.Helper() + + index := map[string]any{ + "updates": []map[string]any{ + { + "channels": []string{"testing", "stable"}, + "version": "202604261712", + "url": "/202604261712", + "files": []map[string]any{ + { + "architecture": "x86_64", + "component": "os", + "filename": "x86_64/IncusOS_202604261712.img.gz", + "sha256": sourceDigest, + "size": sourceSize, + "type": "image-raw", + }, + }, + }, + }, + } + if err := json.NewEncoder(w).Encode(index); err != nil { + tb.Fatalf("write fixture index: %v", err) + } +} + +func verifyPublish(ts *testscript.TestScript, neg bool, args []string) { + if neg { + ts.Fatalf("verify-publish does not support negation") + } + if len(args) != 4 { + ts.Fatalf("usage: verify-publish RESULT_JSON IMAGE VERSION ALIAS") + } + + result := readPublishResult(ts, args[0]) + assertPublishResult(ts, result, args[1], args[2], args[3]) + verifyPublishedArtifact(ts, args[1], args[2], args[3], result.Artifacts[0].ServerArtifactID) +} + +func readPublishResult(ts *testscript.TestScript, path string) publish.ReleaseResult { + var result publish.ReleaseResult + if err := json.Unmarshal([]byte(ts.ReadFile(path)), &result); err != nil { + ts.Fatalf("decode publish result: %v", err) + } + + return result +} + +func assertPublishResult( + ts *testscript.TestScript, + result publish.ReleaseResult, + image string, + publishedVersion string, + alias string, +) { + if result.Image != image { + ts.Fatalf("publish result image = %q, want %q", result.Image, image) + } + if result.Version != publishedVersion { + ts.Fatalf("publish result version = %q, want %q", result.Version, publishedVersion) + } + if result.State != imgsrv.ImageVersionStatePublished { + ts.Fatalf("publish result state = %q, want %q", result.State, imgsrv.ImageVersionStatePublished) + } + if !containsString(result.Aliases, alias) { + ts.Fatalf("publish result aliases = %v, want %q", result.Aliases, alias) + } + if len(result.Artifacts) != 1 { + ts.Fatalf("publish result artifacts length = %d, want 1", len(result.Artifacts)) + } + + artifact := result.Artifacts[0] + if artifact.OperatingSystem != "incusos" || artifact.Architecture != "x86_64" || + artifact.Format != imgsrv.ArtifactFormatRawGZ { + ts.Fatalf("publish result artifact = %+v, want incusos x86_64 raw.gz", artifact) + } +} + +func verifyPublishedArtifact( + ts *testscript.TestScript, + image string, + publishedVersion string, + alias string, + artifactID string, +) { + env, ok := ts.Value(testscriptEnvKey{}).(*imgsrvtest.Env) + if !ok || env == nil { + ts.Fatalf("imgsrv test environment is unavailable") + } + + client, err := imgsrv.New(env.ClientOptions()) + if err != nil { + ts.Fatalf("create imgsrv client: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + manifest, err := client.Catalog().ResolveManifest(ctx, image, alias) + if err != nil { + ts.Fatalf("resolve manifest: %v", err) + } + if len(manifest.Artifacts) != 1 { + ts.Fatalf("manifest artifacts length = %d, want 1", len(manifest.Artifacts)) + } + artifact := manifest.Artifacts[0].Artifact + if artifact.ID.String() != artifactID { + ts.Fatalf("manifest artifact ID = %q, want %q", artifact.ID.String(), artifactID) + } + + download, err := client.Catalog().OpenArtifactDownload( + ctx, + image, + publishedVersion, + artifactID, + imgsrv.OpenBlobOptions{}, + ) + if err != nil { + ts.Fatalf("open artifact download: %v", err) + } + defer download.Body.Close() + + got, err := io.ReadAll(download.Body) + if err != nil { + ts.Fatalf("read artifact download: %v", err) + } + if !bytes.Equal(got, []byte(fixtureArtifact)) { + ts.Fatalf("artifact download = %q, want %q", string(got), fixtureArtifact) + } +} + +type fixtureImageInjector struct{} + +func (fixtureImageInjector) InjectSeed( + _ context.Context, + image incusos.DownloadedImage, + seed incusos.SeedArchive, + outputPath string, +) (incusos.CustomizedImage, error) { + source, err := os.ReadFile(image.Path) + if err != nil { + return incusos.CustomizedImage{}, fmt.Errorf("read fixture source image: %w", err) + } + if !bytes.Equal(source, []byte(fixtureSourceBody)) { + return incusos.CustomizedImage{}, errors.New("fixture source image did not pass through cache") + } + if len(seed.Data) == 0 { + return incusos.CustomizedImage{}, errors.New("fixture seed archive is empty") + } + if err := os.MkdirAll(filepath.Dir(outputPath), 0o750); err != nil { + return incusos.CustomizedImage{}, fmt.Errorf("create fixture output directory: %w", err) + } + + body := []byte(fixtureArtifact) + if err := os.WriteFile(outputPath, body, 0o600); err != nil { + return incusos.CustomizedImage{}, fmt.Errorf("write fixture artifact: %w", err) + } + + return incusos.CustomizedImage{ + Source: image, + Path: outputPath, + Size: int64(len(body)), + SHA256: sha256Hex(body), + }, nil +} + +func containsString(values []string, want string) bool { + for _, value := range values { + if value == want { + return true + } + } + + return false +} + +func sha256Hex(data []byte) string { + sum := sha256.Sum256(data) + return hex.EncodeToString(sum[:]) +} diff --git a/cmd/imgcli/testdata/script/publish.txtar b/cmd/imgcli/testdata/script/publish.txtar new file mode 100644 index 0000000..afff34d --- /dev/null +++ b/cmd/imgcli/testdata/script/publish.txtar @@ -0,0 +1,30 @@ +# Publish an IncusOS image through a local CDN fixture into imgsrv. +exec imgcli publish image.cue --imgsrv-url $IMGSRV_URL --imgsrv-token $IMGSRV_TOKEN --release-version $PUBLISH_VERSION --alias latest --publish-timeout 10s --publish-poll-interval 10ms +stdout '"image":"script-image"' +stdout '"version":"2026.05.06"' +stdout '"state":"published"' +stdout '"aliases":\["latest"\]' +stdout '"operatingSystem":"incusos"' +stdout '"architecture":"x86_64"' +stdout '"format":"raw.gz"' +! stderr . +exists out/script-image-default-amd64.raw.gz +cp stdout publish.json +verify-publish publish.json script-image 2026.05.06 latest + +-- image.cue -- +apiVersion: "imgcli.meigma.io/v0alpha1" +kind: "ImagePlan" +image: name: "script-image" +output: dir: "out" +incusos: { + defaults: source: channel: "testing" + seed: install: {} + variants: default: { + source: version: "202604261712" + artifact: { + architecture: "amd64" + format: "raw.gz" + } + } +} diff --git a/go.mod b/go.mod index 85739ee..529af32 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/lxc/incus-os/incus-osd v0.0.0-20260505023852-d32ba1f13f6f github.com/meigma/imgcli/schemas v0.0.0-20260505154605-5bbbe47a1e06 github.com/meigma/imgsrv v0.0.0-20260507005312-4a67655d031d + github.com/rogpeppe/go-internal v1.14.1 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 @@ -102,7 +103,6 @@ require ( github.com/prometheus/procfs v0.20.1 // indirect github.com/protocolbuffers/txtpbfmt v0.0.0-20260217160748-a481f6a22f94 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/xid v1.6.0 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect @@ -140,6 +140,7 @@ require ( golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.43.0 // indirect golang.org/x/text v0.36.0 // indirect + golang.org/x/tools v0.44.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/internal/cli/build.go b/internal/cli/build.go index c692b9c..9a4cc95 100644 --- a/internal/cli/build.go +++ b/internal/cli/build.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "strings" "github.com/spf13/cobra" @@ -95,7 +96,7 @@ func (rt *runtime) runIncusOSBuild( ) (providers.BuildResult, error) { if rt.usesDefaultIncusOSCache() { var result providers.BuildResult - err := withLockedCache(ctx, rt.config, func( + err := withLockedCache(ctx, rt.config, rt.opts.IncusOSCDNBaseURL, func( catalog incusosprovider.Catalog, downloader incusosprovider.Downloader, ) error { @@ -174,6 +175,7 @@ func newCacheStore(cfg Config) (*cache.DiskStore, error) { func withLockedCache( ctx context.Context, cfg Config, + incusOSCDNBaseURL string, run func(catalog incusosprovider.Catalog, downloader incusosprovider.Downloader) error, ) (err error) { cacheStore, err := newCacheStore(cfg) @@ -191,7 +193,13 @@ func withLockedCache( } }() - client := cdn.NewClient(cdn.WithCacheService(cacheStore)) + cdnOptions := []cdn.Option{ + cdn.WithCacheService(cacheStore), + } + if strings.TrimSpace(incusOSCDNBaseURL) != "" { + cdnOptions = append(cdnOptions, cdn.WithBaseURL(incusOSCDNBaseURL)) + } + client := cdn.NewClient(cdnOptions...) if err := run(client, client); err != nil { return err } diff --git a/internal/cli/build_test.go b/internal/cli/build_test.go index 83c4b43..3f910d1 100644 --- a/internal/cli/build_test.go +++ b/internal/cli/build_test.go @@ -5,6 +5,8 @@ import ( "crypto/sha256" "encoding/hex" "errors" + "net/http" + "net/http/httptest" "os" "path/filepath" "testing" @@ -15,6 +17,7 @@ import ( "github.com/meigma/imgcli/internal/cache" incusosprovider "github.com/meigma/imgcli/internal/providers/incusos" + "github.com/meigma/imgcli/schemas/core" ) func TestWithLockedCachePrunesAfterSuccess(t *testing.T) { @@ -26,7 +29,7 @@ func TestWithLockedCachePrunesAfterSuccess(t *testing.T) { err := withLockedCache(context.Background(), Config{ CacheDir: root, CacheMaxSizeBytes: 4, - }, func(catalog incusosprovider.Catalog, downloader incusosprovider.Downloader) error { + }, "", func(catalog incusosprovider.Catalog, downloader incusosprovider.Downloader) error { require.NotNil(t, catalog) require.NotNil(t, downloader) assertCacheLocked(t, root) @@ -50,7 +53,7 @@ func TestWithLockedCacheSkipsPruneAfterBuildError(t *testing.T) { err := withLockedCache(context.Background(), Config{ CacheDir: root, CacheMaxSizeBytes: 1, - }, func(_ incusosprovider.Catalog, _ incusosprovider.Downloader) error { + }, "", func(_ incusosprovider.Catalog, _ incusosprovider.Downloader) error { assertCacheLocked(t, root) writeBuildTestCachedBlob(t, root, []byte("old!"), time.Date(2026, 5, 1, 10, 0, 0, 0, time.UTC)) return buildErr @@ -61,6 +64,47 @@ func TestWithLockedCacheSkipsPruneAfterBuildError(t *testing.T) { assertCacheUnlocked(t, root) } +func TestWithLockedCacheUsesIncusOSCDNBaseURL(t *testing.T) { + clearIMGCLIEnv(t) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/index.json", r.URL.Path) + _, err := w.Write([]byte(`{ + "updates": [{ + "channels": ["testing"], + "version": "202604261712", + "url": "/202604261712", + "files": [{ + "architecture": "x86_64", + "component": "os", + "filename": "x86_64/IncusOS_202604261712.img.gz", + "sha256": "test-sha", + "size": 42, + "type": "image-raw" + }] + }] + }`)) + assert.NoError(t, err) + })) + t.Cleanup(server.Close) + + err := withLockedCache(context.Background(), Config{ + CacheDir: t.TempDir(), + CacheMaxSizeBytes: 0, + }, server.URL, func(catalog incusosprovider.Catalog, _ incusosprovider.Downloader) error { + asset, resolveErr := catalog.ResolveImage(context.Background(), incusosprovider.ImageQuery{ + Channel: incusosprovider.ChannelTesting, + Version: incusosprovider.Version("202604261712"), + Architecture: core.Architecture("amd64"), + Type: incusosprovider.ImageTypeRaw, + }) + require.NoError(t, resolveErr) + assert.Equal(t, server.URL+"/202604261712/x86_64/IncusOS_202604261712.img.gz", asset.URL) + return nil + }) + + require.NoError(t, err) +} + func assertCacheLocked(t *testing.T, root string) { t.Helper() diff --git a/internal/cli/options.go b/internal/cli/options.go index c077ad6..767d7f6 100644 --- a/internal/cli/options.go +++ b/internal/cli/options.go @@ -37,6 +37,9 @@ type Options struct { // IncusOSImageInjector writes IncusOS seed archives into source images. Nil selects the default image injector. IncusOSImageInjector incusos.ImageInjector + // IncusOSCDNBaseURL overrides the default IncusOS CDN URL. Empty selects the upstream CDN. + IncusOSCDNBaseURL string + // ImgsrvUploadsClient uploads artifacts to imgsrv. Nil selects the HTTP SDK client. ImgsrvUploadsClient publish.UploadsClient diff --git a/internal/integration/publish_flow_test.go b/internal/integration/publish_flow_test.go deleted file mode 100644 index 3583996..0000000 --- a/internal/integration/publish_flow_test.go +++ /dev/null @@ -1,227 +0,0 @@ -//go:build integration - -package integration_test - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "io" - "os" - "path/filepath" - "testing" - - imgsrv "github.com/meigma/imgsrv/client" - imgsrvtest "github.com/meigma/imgsrv/test" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/meigma/imgcli/internal/cli" - "github.com/meigma/imgcli/internal/providers/incusos" - "github.com/meigma/imgcli/internal/publish" - "github.com/meigma/imgcli/schemas/core" -) - -const integrationAPIToken = "testtok.imgcli-publish" - -func TestPublishIncusOSReleaseToImgsrv(t *testing.T) { - clearIntegrationEnv(t) - ctx := context.Background() - env := imgsrvtest.Start(t, imgsrvtest.WithCASPromotion(), imgsrvtest.WithAPIToken(integrationAPIToken)) - imageName := "incusos-test" - version := "2026.05.06" - outputDir := filepath.Join(t.TempDir(), "out") - configPath := writeIntegrationConfig(t, imageName, outputDir) - artifactBody := []byte("published IncusOS artifact bytes") - catalog := &integrationCatalog{ - asset: incusos.ImageAsset{ - Version: incusos.Version("202604261712"), - Architecture: core.Architecture("amd64"), - Type: incusos.ImageTypeRaw, - URL: "https://example.invalid/os/202604261712/x86_64/IncusOS_202604261712.img.gz", - SHA256: "source-sha", - Size: 42, - }, - } - downloader := &integrationDownloader{ - image: incusos.DownloadedImage{ - Path: "/cache/source.img.gz", - SHA256: "source-sha", - Size: 42, - }, - } - seedBuilder := &integrationSeedBuilder{seed: incusos.SeedArchive{Data: []byte("seed")}} - injector := &integrationInjector{body: artifactBody} - var stdout bytes.Buffer - var stderr bytes.Buffer - - cmd, err := cli.NewRootCommand(cli.Options{ - Stdout: &stdout, - Stderr: &stderr, - Stdin: bytes.NewReader(nil), - Environ: []string{"TERM=dumb"}, - IncusOSCatalog: catalog, - IncusOSDownloader: downloader, - IncusOSSeedBuilder: seedBuilder, - IncusOSImageInjector: injector, - }) - require.NoError(t, err) - cmd.SetArgs([]string{ - "publish", - configPath, - "--imgsrv-url", - env.BaseURL(), - "--imgsrv-token", - integrationAPIToken, - "--release-version", - version, - "--alias", - "latest", - "--publish-timeout", - "10s", - "--publish-poll-interval", - "10ms", - }) - - require.NoError(t, cmd.ExecuteContext(ctx)) - assert.Empty(t, stderr.String()) - var published publish.ReleaseResult - require.NoError(t, json.Unmarshal(stdout.Bytes(), &published)) - assert.Equal(t, imageName, published.Image) - assert.Equal(t, version, published.Version) - assert.Equal(t, imgsrv.ImageVersionStatePublished, published.State) - assert.Equal(t, []string{"latest"}, published.Aliases) - require.Len(t, published.Artifacts, 1) - assert.Equal(t, "incusos", published.Artifacts[0].OperatingSystem) - assert.Equal(t, "x86_64", published.Artifacts[0].Architecture) - assert.Equal(t, imgsrv.ArtifactFormatRawGZ, published.Artifacts[0].Format) - - imgsrvClient := env.Client(t) - manifest, err := imgsrvClient.Catalog().ResolveManifest(ctx, imageName, "latest") - require.NoError(t, err) - require.Len(t, manifest.Artifacts, 1) - artifact := manifest.Artifacts[0].Artifact - assert.Equal(t, imgsrv.ArtifactFormatRawGZ, artifact.Format) - assert.Equal(t, "x86_64", artifact.Architecture) - assert.Equal(t, "incusos", artifact.OperatingSystem) - - download, err := imgsrvClient.Catalog().OpenArtifactDownload( - ctx, - imageName, - version, - artifact.ID.String(), - imgsrv.OpenBlobOptions{}, - ) - require.NoError(t, err) - defer download.Body.Close() - got, err := io.ReadAll(download.Body) - require.NoError(t, err) - assert.Equal(t, artifactBody, got) -} - -func clearIntegrationEnv(t *testing.T) { - t.Helper() - - for _, key := range []string{ - "IMGCLI_CACHE_DIR", - "IMGCLI_CACHE_MAX_SIZE", - "IMGCLI_CONFIG", - "IMGCLI_IMGSRV_TOKEN", - "IMGCLI_IMGSRV_URL", - "IMGCLI_LOG_LEVEL", - "IMGCLI_LOG_FORMAT", - "IMGCLI_NO_COLOR", - "IMGCLI_PUBLISH_PART_SIZE", - "IMGCLI_PUBLISH_POLL_INTERVAL", - "IMGCLI_PUBLISH_TIMEOUT", - "IMGCLI_PUBLISH_VERSION", - "IMGCLI_PUBLISH_WAIT", - } { - t.Setenv(key, "") - } - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) -} - -func writeIntegrationConfig(t *testing.T, imageName string, outputDir string) string { - t.Helper() - - path := filepath.Join(t.TempDir(), "image.cue") - content := ` -apiVersion: "imgcli.meigma.io/v0alpha1" -kind: "ImagePlan" -image: name: "` + imageName + `" -output: dir: "` + outputDir + `" -incusos: { - defaults: source: channel: "testing" - seed: install: {} - variants: default: { - source: version: "202604261712" - artifact: { - architecture: "amd64" - format: "raw.gz" - } - } -} -` - require.NoError(t, os.WriteFile(path, []byte(content), 0o600)) - return path -} - -type integrationCatalog struct { - asset incusos.ImageAsset -} - -func (c *integrationCatalog) ResolveImage(_ context.Context, query incusos.ImageQuery) (incusos.ImageAsset, error) { - c.asset.Architecture = query.Architecture - c.asset.Type = query.Type - return c.asset, nil -} - -type integrationDownloader struct { - image incusos.DownloadedImage -} - -func (d *integrationDownloader) DownloadImage( - _ context.Context, - asset incusos.ImageAsset, -) (incusos.DownloadedImage, error) { - image := d.image - image.Asset = asset - return image, nil -} - -type integrationSeedBuilder struct { - seed incusos.SeedArchive -} - -func (b *integrationSeedBuilder) BuildSeed(_ context.Context, _ incusos.Config) (incusos.SeedArchive, error) { - return b.seed, nil -} - -type integrationInjector struct { - body []byte -} - -func (i *integrationInjector) InjectSeed( - _ context.Context, - image incusos.DownloadedImage, - _ incusos.SeedArchive, - outputPath string, -) (incusos.CustomizedImage, error) { - if err := os.MkdirAll(filepath.Dir(outputPath), 0o750); err != nil { - return incusos.CustomizedImage{}, err - } - if err := os.WriteFile(outputPath, i.body, 0o600); err != nil { - return incusos.CustomizedImage{}, err - } - - sum := sha256.Sum256(i.body) - return incusos.CustomizedImage{ - Source: image, - Path: outputPath, - Size: int64(len(i.body)), - SHA256: hex.EncodeToString(sum[:]), - }, nil -} diff --git a/moon.yml b/moon.yml index bf59d19..57f37b8 100644 --- a/moon.yml +++ b/moon.yml @@ -27,6 +27,8 @@ fileGroups: go-sources: - 'cmd/**/*.go' - 'internal/**/*.go' + test-fixtures: + - 'cmd/**/testdata/**/*' schema-config: - '.prototools' - '.moon/proto/cue.toml' @@ -88,11 +90,12 @@ tasks: test-integration: deps: - 'root:schemas-generate-check' - command: 'go test -count 1 -tags integration ./internal/integration' + command: 'go test -count 1 -tags integration ./cmd/imgcli' toolchains: ['go'] inputs: - '@group(go-config)' - '@group(go-sources)' + - '@group(test-fixtures)' options: cache: false