Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion .github/instructions/testing.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ to verify specific call expectations.

## Lock Files in Tests

Use `env.WriteLock(t, name, lock)` to create lock files on the test filesystem:
For unit tests using `testutils.NewTestEnv(t)`, use `env.WriteLock(t, name, lock)`
to create lock files on the in-memory test filesystem:

```go
lock := lockfile.New()
Expand All @@ -153,6 +154,24 @@ lock.ManualBump = 1
env.WriteLock(t, "curl", lock)
```

For scenario project fixtures, use `projecttest.AddLock(...)` to include a lock
file in the dynamic project when it is first created (the lock is serialized as
part of the initial project fixture). Use `projecttest.WriteLock(...)` to save a
lock file into an already-serialized project directory on disk — for example,
when a test needs to change a component's lock contents and commit that change as
a new git commit:

```go
// AddLock: seed the initial lock when the project fixture is built.
projecttest.NewDynamicTestProject(
projecttest.AddLock("curl", projecttest.WithLockInputFingerprint("sha256:v1")),
)

// WriteLock: write updated lock contents to disk in a later step
// (e.g. before staging a new commit).
projecttest.WriteLock(t, projectDir, "curl", projecttest.WithLockInputFingerprint("sha256:v2"))
Comment thread
Tonisal-byte marked this conversation as resolved.
```

## Mocking External Commands

`CmdFactory.RunHandler` and `RunAndGetOutputHandler` intercept ALL external
Expand Down
1 change: 1 addition & 0 deletions scenario/component_build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func TestBuildingLocalComponent(t *testing.T) {
spec := projecttest.NewSpec(projecttest.WithBuildArch(projecttest.NoArch))
project := projecttest.NewDynamicTestProject(
projecttest.AddSpec(spec),
projecttest.AddLock(spec.GetName(), projecttest.WithLockInputFingerprint("sha256:"+spec.GetName()+"-v1")),
projecttest.UseTestDefaultConfigs(),
projecttest.WithGitRepo(),
)
Expand Down
60 changes: 24 additions & 36 deletions scenario/component_changed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package scenario_tests

import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -88,6 +87,13 @@ func writeFileInDir(t *testing.T, dir, relPath, content string) {
require.NoError(t, os.WriteFile(absPath, []byte(content), fileperms.PublicFile))
}

// writeLockInDir writes a simple fingerprint-only lock file for scenario tests.
func writeLockInDir(t *testing.T, dir, componentName, inputFingerprint string) {
t.Helper()

projecttest.WriteLock(t, dir, componentName, projecttest.WithLockInputFingerprint(inputFingerprint))
}

// TestComponentChanged_E2E exercises the full `azldev component changed` command
// with a real git repository, verifying JSON output for multi-component change
// detection.
Expand Down Expand Up @@ -157,20 +163,16 @@ func TestComponentChanged_E2E(t *testing.T) {
gitInDir(t, projectDir, "config", "user.email", "test@test.com")
gitInDir(t, projectDir, "config", "user.name", "Test")

lockV1Curl := fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:curl-v1")
lockV1Bash := fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:bash-v1")

writeFileInDir(t, projectDir, "locks/curl.lock", lockV1Curl)
writeFileInDir(t, projectDir, "locks/bash.lock", lockV1Bash)
writeLockInDir(t, projectDir, "curl", "sha256:curl-v1")
writeLockInDir(t, projectDir, "bash", "sha256:bash-v1")

gitInDir(t, projectDir, "add", ".")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "initial")

fromRef := gitInDir(t, projectDir, "rev-parse", "HEAD")

// Second commit: change curl's lock, leave bash unchanged.
lockV2Curl := fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:curl-v2")
writeFileInDir(t, projectDir, "locks/curl.lock", lockV2Curl)
writeLockInDir(t, projectDir, "curl", "sha256:curl-v2")

gitInDir(t, projectDir, "add", "locks/curl.lock")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "update curl")
Expand Down Expand Up @@ -243,8 +245,7 @@ func TestComponentChanged_SameRef(t *testing.T) {
gitInDir(t, projectDir, "config", "user.email", "test@test.com")
gitInDir(t, projectDir, "config", "user.name", "Test")

lockContent := fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:v1")
writeFileInDir(t, projectDir, "locks/curl.lock", lockContent)
writeLockInDir(t, projectDir, "curl", "sha256:v1")

gitInDir(t, projectDir, "add", ".")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "initial")
Expand Down Expand Up @@ -387,26 +388,23 @@ func TestComponentChanged_SourcesChange(t *testing.T) {
)

// Commit 1: initial lock + rendered sources.
writeFileInDir(t, projectDir, "locks/curl.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:v1"))
writeLockInDir(t, projectDir, "curl", "sha256:v1")
writeFileInDir(t, projectDir, "specs/c/curl/sources",
"SHA512 (curl-8.0.tar.gz) = aaa111")
gitInDir(t, projectDir, "add", ".")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "initial")
ref1 := gitInDir(t, projectDir, "rev-parse", "HEAD")

// Commit 2: change lock fingerprint AND sources (new tarball).
writeFileInDir(t, projectDir, "locks/curl.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:v2"))
writeLockInDir(t, projectDir, "curl", "sha256:v2")
writeFileInDir(t, projectDir, "specs/c/curl/sources",
"SHA512 (curl-8.1.tar.gz) = bbb222")
gitInDir(t, projectDir, "add", ".")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "update sources")
ref2 := gitInDir(t, projectDir, "rev-parse", "HEAD")

// Commit 3: change lock fingerprint only (config tweak, same tarball).
writeFileInDir(t, projectDir, "locks/curl.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:v3"))
writeLockInDir(t, projectDir, "curl", "sha256:v3")
gitInDir(t, projectDir, "add", ".")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "config change")

Expand Down Expand Up @@ -460,15 +458,13 @@ func TestComponentChanged_InvertedRefs(t *testing.T) {
)

// Commit 1: v1 lock.
writeFileInDir(t, projectDir, "locks/curl.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:old"))
writeLockInDir(t, projectDir, "curl", "sha256:old")
gitInDir(t, projectDir, "add", ".")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "v1")
oldRef := gitInDir(t, projectDir, "rev-parse", "HEAD")

// Commit 2: v2 lock.
writeFileInDir(t, projectDir, "locks/curl.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:new"))
writeLockInDir(t, projectDir, "curl", "sha256:new")
gitInDir(t, projectDir, "add", ".")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "v2")
newRef := gitInDir(t, projectDir, "rev-parse", "HEAD")
Expand Down Expand Up @@ -526,8 +522,7 @@ func TestComponentChanged_NewComponent(t *testing.T) {
fromRef := gitInDir(t, projectDir, "rev-parse", "HEAD")

// Commit 2: add curl lock.
writeFileInDir(t, projectDir, "locks/curl.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:first"))
writeLockInDir(t, projectDir, "curl", "sha256:first")
gitInDir(t, projectDir, "add", ".")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "add curl")

Expand Down Expand Up @@ -571,10 +566,8 @@ func TestComponentChanged_DeletedComponent(t *testing.T) {
)

// Commit 1: lock files for curl (in config) and oldpkg (NOT in config).
writeFileInDir(t, projectDir, "locks/curl.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:curl-v1"))
writeFileInDir(t, projectDir, "locks/oldpkg.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:oldpkg-v1"))
writeLockInDir(t, projectDir, "curl", "sha256:curl-v1")
writeLockInDir(t, projectDir, "oldpkg", "sha256:oldpkg-v1")
gitInDir(t, projectDir, "add", ".")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "initial")
fromRef := gitInDir(t, projectDir, "rev-parse", "HEAD")
Expand Down Expand Up @@ -643,21 +636,17 @@ func TestComponentChanged_JSONContract(t *testing.T) {
)

// Commit 1: curl has lock + sources, bash has lock, oldpkg has lock (not in config).
writeFileInDir(t, projectDir, "locks/curl.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:curl-v1"))
writeFileInDir(t, projectDir, "locks/bash.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:bash-v1"))
writeFileInDir(t, projectDir, "locks/oldpkg.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:old-v1"))
writeLockInDir(t, projectDir, "curl", "sha256:curl-v1")
writeLockInDir(t, projectDir, "bash", "sha256:bash-v1")
writeLockInDir(t, projectDir, "oldpkg", "sha256:old-v1")
writeFileInDir(t, projectDir, "specs/c/curl/sources",
"SHA512 (curl-1.0.tar.gz) = aaa")
gitInDir(t, projectDir, "add", ".")
gitInDir(t, projectDir, "-c", "commit.gpgsign=false", "commit", "-m", "initial")
fromRef := gitInDir(t, projectDir, "rev-parse", "HEAD")

// Commit 2: curl fingerprint + sources changed, bash unchanged, oldpkg removed.
writeFileInDir(t, projectDir, "locks/curl.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:curl-v2"))
writeLockInDir(t, projectDir, "curl", "sha256:curl-v2")
writeFileInDir(t, projectDir, "specs/c/curl/sources",
"SHA512 (curl-2.0.tar.gz) = bbb")
gitInDir(t, projectDir, "rm", "locks/oldpkg.lock")
Expand Down Expand Up @@ -762,8 +751,7 @@ func TestComponentChanged_IntegrityViolation(t *testing.T) {
)

// Commit 1: lock + matching rendered sources.
writeFileInDir(t, projectDir, "locks/curl.lock",
fmt.Sprintf("version = 1\ninput-fingerprint = %q\n", "sha256:v1"))
writeLockInDir(t, projectDir, "curl", "sha256:v1")
writeFileInDir(t, projectDir, "specs/c/curl/sources",
"SHA512 (curl-8.0.tar.gz) = aaa111")
gitInDir(t, projectDir, "add", ".")
Expand Down
1 change: 1 addition & 0 deletions scenario/component_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func TestQueryingAComponent(t *testing.T) {
// Create a simple project with the spec, using test default configs for distro and mock configurations.
project := projecttest.NewDynamicTestProject(
projecttest.AddSpec(spec),
projecttest.AddLock(spec.GetName(), projecttest.WithLockInputFingerprint("sha256:"+spec.GetName()+"-v1")),
projecttest.UseTestDefaultConfigs(),
)

Expand Down
24 changes: 16 additions & 8 deletions scenario/component_render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func localComponentConfig(name string, overlays ...projectconfig.ComponentOverla
}
}

func localComponentLock(name string) projecttest.DynamicTestProjectOption {
return projecttest.AddLock(name, projecttest.WithLockInputFingerprint("sha256:"+name+"-v1"))
}

func TestRenderSimpleLocalSpec(t *testing.T) {
t.Parallel()

Expand All @@ -49,6 +53,7 @@ func TestRenderSimpleLocalSpec(t *testing.T) {
project := projecttest.NewDynamicTestProject(
projecttest.AddSpec(spec),
projecttest.AddComponent(localComponentConfig("test-render")),
localComponentLock("test-render"),
projecttest.UseTestDefaultConfigs(),
projecttest.WithGitRepo(),
)
Expand Down Expand Up @@ -99,6 +104,7 @@ func TestRenderWithConfiguredOutputDir(t *testing.T) {
project := projecttest.NewDynamicTestProject(
projecttest.AddSpec(spec),
projecttest.AddComponent(localComponentConfig("config-test")),
localComponentLock("config-test"),
projecttest.UseTestDefaultConfigs(),
projecttest.WithGitRepo(),
// Set rendered-specs-dir in project config instead of using -o.
Expand Down Expand Up @@ -149,6 +155,7 @@ func TestRenderWithOverlayApplied(t *testing.T) {
Value: "test-overlay-dep",
},
)),
localComponentLock("test-overlay"),
projecttest.UseTestDefaultConfigs(),
projecttest.WithGitRepo(),
)
Expand Down Expand Up @@ -205,6 +212,7 @@ func TestRenderWithPatchSidecar(t *testing.T) {
Source: "patches/fix-stuff.patch",
},
)),
localComponentLock("test-patch"),
projecttest.AddFile("patches/fix-stuff.patch", patchContent),
projecttest.UseTestDefaultConfigs(),
projecttest.WithGitRepo(),
Expand Down Expand Up @@ -251,6 +259,7 @@ func TestRenderStaleCleanup(t *testing.T) {
project := projecttest.NewDynamicTestProject(
projecttest.AddSpec(spec),
projecttest.AddComponent(localComponentConfig("keep-me")),
localComponentLock("keep-me"),
projecttest.UseTestDefaultConfigs(),
projecttest.WithGitRepo(),
projecttest.AddFile("SPECS/s/stale-component/RENDER_FAILED", "Rendering failed.\n"),
Expand Down Expand Up @@ -297,6 +306,7 @@ func TestRenderRefusesOverwriteWithoutForce(t *testing.T) {
project := projecttest.NewDynamicTestProject(
projecttest.AddSpec(spec),
projecttest.AddComponent(localComponentConfig("no-clobber")),
localComponentLock("no-clobber"),
projecttest.UseTestDefaultConfigs(),
projecttest.WithGitRepo(),
projecttest.AddFile("SPECS/n/no-clobber/existing-file.txt", "do not delete me\n"),
Expand Down Expand Up @@ -371,6 +381,7 @@ License: MIT

project := projecttest.NewDynamicTestProject(
projecttest.AddComponent(localComponentConfig("golang-example")),
localComponentLock("golang-example"),
// Write the custom spec content directly via AddFile since AddSpec's
// TestSpec renderer doesn't support %gometa.
projecttest.AddFile("specs/golang-example/golang-example.spec", goSpecContent),
Expand Down Expand Up @@ -438,6 +449,8 @@ func TestRenderMultipleComponentsParallel(t *testing.T) {
projecttest.AddSpec(specB),
projecttest.AddComponent(localComponentConfig("comp-alpha")),
projecttest.AddComponent(localComponentConfig("comp-beta")),
localComponentLock("comp-alpha"),
localComponentLock("comp-beta"),
projecttest.UseTestDefaultConfigs(),
projecttest.WithGitRepo(),
)
Expand Down Expand Up @@ -502,9 +515,11 @@ func TestRenderBrokenSpecWithGoodSpec(t *testing.T) {
project := projecttest.NewDynamicTestProject(
projecttest.AddSpec(goodSpec),
projecttest.AddComponent(localComponentConfig("good-pkg")),
localComponentLock("good-pkg"),
// Add a broken spec as a raw file — not valid RPM spec syntax.
projecttest.AddFile("specs/broken-pkg/broken-pkg.spec", "this is not a valid spec file\n"),
projecttest.AddComponent(localComponentConfig("broken-pkg")),
localComponentLock("broken-pkg"),
projecttest.UseTestDefaultConfigs(),
projecttest.WithGitRepo(),
)
Expand Down Expand Up @@ -569,13 +584,6 @@ func TestRenderLocalSpecWithSyntheticHistory(t *testing.T) {
projecttest.WithBuildArch(projecttest.NoArch),
)

// Pre-baked lock file with a stale fingerprint. The overlay in the
// component config changes the runtime fingerprint, so dirty detection
// will fire and add a synthetic commit.
const lockFileContent = `version = 1
input-fingerprint = "pre-baked-for-test"
`

project := projecttest.NewDynamicTestProject(
projecttest.AddSpec(spec),
projecttest.AddComponent(localComponentConfig("synth-local",
Expand All @@ -587,7 +595,7 @@ input-fingerprint = "pre-baked-for-test"
},
)),
projecttest.UseTestDefaultConfigs(),
projecttest.AddFile("locks/synth-local.lock", lockFileContent),
projecttest.AddLock("synth-local", projecttest.WithLockInputFingerprint("pre-baked-for-test")),
projecttest.WithGitRepo(),
)

Expand Down
Loading
Loading