Skip to content

Commit 6c85658

Browse files
authored
refactor: move defaultconfigs -> test only (#398)
* Moved distro configuration files from `defaultconfigs/content/` to `scenario/testdata/defaultconfigs/` * Updated test infrastructure to optionally include test default configs via opt-in * Modified all scenario build tests to use the new test default configs
1 parent d2e2ccf commit 6c85658

17 files changed

Lines changed: 192 additions & 92 deletions
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,2 @@
1-
# Include distro config.
2-
includes = ["distros/**/*.distro.toml"]
3-
4-
[project]
5-
default-distro = { name = "azurelinux", version = "3.0" }
6-
71
[tools.imageCustomizer]
82
containerTag = "mcr.microsoft.com/azurelinux/imagecustomizer:0.18.0"

scenario/component_build_test.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,15 @@ func TestBuildingLocalComponent(t *testing.T) {
2929
}
3030

3131
// Create a simple project with a simple noarch spec.
32+
// Include test default configs to get distro and mock configurations.
3233
spec := projecttest.NewSpec(projecttest.WithBuildArch(projecttest.NoArch))
33-
project := projecttest.NewDynamicTestProject(projecttest.AddSpec(spec))
34+
project := projecttest.NewDynamicTestProject(
35+
projecttest.AddSpec(spec),
36+
projecttest.UseTestDefaultConfigs(),
37+
)
3438

35-
// Run the build.
36-
results := buildtest.NewBuildTest(project, spec.GetName()).Run(t)
39+
// Run the build with test default configs copied into the container.
40+
results := buildtest.NewBuildTest(project, spec.GetName(), projecttest.WithTestDefaultConfigs()).Run(t)
3741

3842
// Make sure we got 1 SRPM.
3943
srpms := results.GetSRPMs(t)
@@ -69,10 +73,13 @@ func TestBuildingLocalComponentFromCheckedInFiles(t *testing.T) {
6973
}
7074

7175
// Create a simple project with a simple noarch spec.
72-
project := projecttest.NewTemplatedTestProject(t, "testdata/simple")
76+
// Include test default configs to get distro and mock configurations.
77+
project := projecttest.NewTemplatedTestProject(t, "testdata/simple",
78+
projecttest.TemplatedUseTestDefaultConfigs(),
79+
)
7380

74-
// Run the build.
75-
results := buildtest.NewBuildTest(project, "a").Run(t)
81+
// Run the build with test default configs copied into the container.
82+
results := buildtest.NewBuildTest(project, "a", projecttest.WithTestDefaultConfigs()).Run(t)
7683

7784
// Make sure we got 1 SRPM.
7885
srpms := results.GetSRPMs(t)
@@ -110,12 +117,14 @@ func TestBuildingUpstreamComponent(t *testing.T) {
110117

111118
// Create a project with a well-known component available in Fedora; we rely on the
112119
// default configuration defaulting to sourcing from upstream.
120+
// Include test default configs to get distro and mock configurations.
113121
project := projecttest.NewDynamicTestProject(
114122
projecttest.AddComponent(&projectconfig.ComponentConfig{Name: testComponentName}),
123+
projecttest.UseTestDefaultConfigs(),
115124
)
116125

117-
// Run the build.
118-
results := buildtest.NewBuildTest(project, testComponentName).Run(t)
126+
// Run the build with test default configs copied into the container.
127+
results := buildtest.NewBuildTest(project, testComponentName, projecttest.WithTestDefaultConfigs()).Run(t)
119128

120129
// Make sure we got 1 SRPM.
121130
srpms := results.GetSRPMs(t)

scenario/component_query_test.go

Lines changed: 21 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,9 @@
66
package scenario_tests
77

88
import (
9-
"encoding/json"
10-
"os"
11-
"path/filepath"
12-
"strings"
139
"testing"
1410

15-
"github.com/gim-home/azldev-preview/scenario/internal/cmdtest"
11+
"github.com/gim-home/azldev-preview/scenario/internal/projecttest"
1612
"github.com/stretchr/testify/assert"
1713
"github.com/stretchr/testify/require"
1814
)
@@ -26,61 +22,35 @@ func TestQueryingAComponent(t *testing.T) {
2622
t.Skip("skipping long test")
2723
}
2824

29-
// Write out a simple script that writes a spec file and tries to query it.
30-
testScript := `
31-
set -euxo pipefail
32-
azldev project init
33-
ln -s /var/lib/mock build # Ensure the build output dir is writable by mock
25+
// Create a simple spec with a known name and version.
26+
spec := projecttest.NewSpec(
27+
projecttest.WithName("test-component"),
28+
projecttest.WithVersion("3.1.4.159"),
29+
)
3430

35-
cat <<EOF >test-component.spec
36-
Name: test-component
37-
Version: 3.1.4.159
38-
Release: 1%{?dist}
39-
Summary: A test component
40-
License: MIT
31+
// Create a simple project with the spec, using test default configs for distro and mock configurations.
32+
project := projecttest.NewDynamicTestProject(
33+
projecttest.AddSpec(spec),
34+
projecttest.UseTestDefaultConfigs(),
35+
)
4136

42-
%description
43-
Test component for, you know, testing.
44-
EOF
37+
// Run the component query command with test default configs copied into the container.
38+
results := projecttest.NewProjectTest(
39+
project,
40+
[]string{"component", "query", spec.GetName()},
41+
projecttest.WithTestDefaultConfigs(),
42+
).RunInContainer(t)
4543

46-
ls -l -R
47-
48-
azldev component list --all-components
49-
50-
azldev -v component query test-component --output-format json >query-result.json
51-
`
52-
53-
// NOTE: We need to run in a privileged container so 'mock' can create its nested root environment.
54-
// NOTE: We need to enable networking so 'mock' can download Azure Linux packages to build a root.
55-
results, err := cmdtest.NewScenarioTest().
56-
WithScript(strings.NewReader(testScript)).
57-
InContainer().
58-
WithPrivilege().
59-
WithNetwork().
60-
Run(t)
61-
62-
require.NoError(t, err)
63-
results.AssertZeroExitCode(t)
64-
65-
// Find the output file.
66-
outputFilePath := filepath.Join(results.Workdir, "query-result.json")
67-
require.FileExists(t, outputFilePath, "Expected output file to exist")
68-
69-
// Read it.
70-
bytes, err := os.ReadFile(outputFilePath)
71-
require.NoError(t, err, "Failed to read output file")
72-
73-
// Parse it as JSON
74-
var output []map[string]interface{}
75-
require.NoError(t, json.Unmarshal(bytes, &output))
44+
// Get the parsed JSON output.
45+
output := results.GetJSONResult()
7646

7747
// There should just be one component in the output.
7848
require.Len(t, output, 1, "Expected one component in the output")
7949
componentOutput := output[0]
8050

8151
// Check for name.
8252
require.Contains(t, componentOutput, "Name")
83-
assert.Equal(t, componentOutput["Name"], "test-component", "Expected component name to match")
53+
assert.Equal(t, spec.GetName(), componentOutput["Name"], "Expected component name to match")
8454

8555
// Check for EVR structure.
8656
require.Contains(t, componentOutput, "Version")
@@ -90,5 +60,5 @@ azldev -v component query test-component --output-format json >query-result.json
9060
versionMap, ok := version.(map[string]interface{})
9161
require.True(t, ok, "Version field is not a map")
9262
require.Contains(t, versionMap, "Version")
93-
assert.Equal(t, versionMap["Version"], "3.1.4.159")
63+
assert.Equal(t, spec.GetVersion(), versionMap["Version"])
9464
}

scenario/internal/buildtest/buildtest.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,14 @@ type BuildTestResults struct {
2727
}
2828

2929
// NewBuildTest creates a new [BuildTest] for the specified project and component name.
30-
func NewBuildTest(project projecttest.TestProject, componentName string) *BuildTest {
31-
projectTest := projecttest.NewProjectTest(project, []string{"component", "build", componentName})
30+
// Optional [projecttest.ProjectTestOption]s can be passed to configure the underlying project test,
31+
// such as [projecttest.WithTestDefaultConfigs] to include the test default configs.
32+
func NewBuildTest(
33+
project projecttest.TestProject,
34+
componentName string,
35+
options ...projecttest.ProjectTestOption,
36+
) *BuildTest {
37+
projectTest := projecttest.NewProjectTest(project, []string{"component", "build", componentName}, options...)
3238

3339
return &BuildTest{
3440
inner: projectTest,

scenario/internal/projecttest/dynamictestproject.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ func AddComponent(componentConfig *projectconfig.ComponentConfig) DynamicTestPro
8787
}
8888
}
8989

90+
// UseTestDefaultConfigs configures the project to include the test default configs.
91+
// This adds an include directive to the project's azldev.toml that references the test
92+
// default configs (which must be copied into the container separately using
93+
// [WithTestDefaultConfigs] on the [ProjectTest]).
94+
func UseTestDefaultConfigs() DynamicTestProjectOption {
95+
return func(p *dynamicTestProject) {
96+
// Prepend the test default configs include path so it's loaded first.
97+
// Project-specific settings will override it.
98+
p.configFile.Includes = append([]string{TestDefaultConfigsIncludePath}, p.configFile.Includes...)
99+
}
100+
}
101+
90102
func (p *dynamicTestProject) addSpecContents(name, specContent string) {
91103
// Place specs in their own dir.
92104
relativeFilePath := filepath.Join("specs", name, name+".spec")

scenario/internal/projecttest/projecttest.go

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"os"
1010
"path/filepath"
11+
"runtime"
1112
"strings"
1213
"testing"
1314

@@ -17,21 +18,62 @@ import (
1718
"github.com/stretchr/testify/require"
1819
)
1920

20-
const projectSubdir = "project"
21+
const (
22+
projectSubdir = "project"
23+
24+
// TestDefaultConfigsSubdir is the subdirectory name used for test default configs in the container.
25+
TestDefaultConfigsSubdir = "testdefaults"
26+
27+
// TestDefaultConfigsIncludePath is the relative include path to use from a project's azldev.toml
28+
// to include the test default configs. This path is relative to the project directory.
29+
TestDefaultConfigsIncludePath = "../" + TestDefaultConfigsSubdir + "/defaults.toml"
30+
)
31+
32+
// TestDefaultConfigsDir returns the absolute path to the scenario/testdata/defaultconfigs directory.
33+
// This function uses runtime.Caller to resolve the path relative to this source file's location.
34+
func TestDefaultConfigsDir() string {
35+
// Get the path of this source file.
36+
_, thisFile, _, ok := runtime.Caller(0)
37+
if !ok {
38+
panic("failed to get caller information for TestDefaultConfigsDir")
39+
}
40+
41+
// Navigate from scenario/internal/projecttest/ up to scenario/testdata/defaultconfigs/
42+
scenarioDir := filepath.Dir(filepath.Dir(filepath.Dir(thisFile)))
43+
44+
return filepath.Join(scenarioDir, "testdata", "defaultconfigs")
45+
}
2146

2247
// ProjectTest represents a runnable, project-oriented test case.
2348
type ProjectTest struct {
24-
project TestProject
25-
commandArgs []string
49+
project TestProject
50+
commandArgs []string
51+
useTestDefaultConfigs bool
52+
}
53+
54+
// ProjectTestOption is a function that can be used to configure a [ProjectTest].
55+
type ProjectTestOption func(*ProjectTest)
56+
57+
// WithTestDefaultConfigs configures the project test to copy and use the test default configs.
58+
// When enabled, the test default configs from scenario/testdata/defaultconfigs/ will be
59+
// copied into the container and made available for the project to include.
60+
func WithTestDefaultConfigs() ProjectTestOption {
61+
return func(p *ProjectTest) {
62+
p.useTestDefaultConfigs = true
63+
}
2664
}
2765

2866
// NewProjectTest creates a new [ProjectTest] with the specified project and command arguments.
29-
func NewProjectTest(project TestProject, commandArgs []string) *ProjectTest {
67+
func NewProjectTest(project TestProject, commandArgs []string, options ...ProjectTestOption) *ProjectTest {
3068
params := &ProjectTest{
3169
project: project,
3270
commandArgs: commandArgs,
3371
}
3472

73+
for _, option := range options {
74+
option(params)
75+
}
76+
3577
return params
3678
}
3779

@@ -64,9 +106,18 @@ azldev -C project -v %s --output-format json >result.json
64106

65107
// NOTE: We need to run in a privileged container so 'mock' can create its nested root environment.
66108
// NOTE: We need to enable networking so 'mock' can download Azure Linux packages to build a root.
67-
results, err := cmdtest.NewScenarioTest().
109+
scenarioTest := cmdtest.NewScenarioTest().
68110
WithScript(strings.NewReader(testScript)).
69-
AddDirRecursive(t, projectSubdir, projectStagingDir).
111+
AddDirRecursive(t, projectSubdir, projectStagingDir)
112+
113+
// If test default configs are requested, copy them into the container as a sibling directory
114+
// to the project directory. This allows the project's azldev.toml to include them via a
115+
// relative path like "../testdefaults/defaults.toml".
116+
if p.useTestDefaultConfigs {
117+
scenarioTest.AddDirRecursive(t, TestDefaultConfigsSubdir, TestDefaultConfigsDir())
118+
}
119+
120+
results, err := scenarioTest.
70121
InContainer().
71122
WithPrivilege().
72123
WithNetwork().

scenario/internal/projecttest/templatetestproject.go

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,37 @@ import (
99
"runtime"
1010
"testing"
1111

12+
"github.com/gim-home/azldev-preview/internal/projectconfig"
13+
"github.com/gim-home/azldev-preview/internal/utils/fileperms"
1214
"github.com/gim-home/azldev-preview/internal/utils/fileutils"
15+
"github.com/pelletier/go-toml/v2"
1316
"github.com/spf13/afero"
1417
"github.com/stretchr/testify/require"
1518
)
1619

20+
// TemplatedTestProjectOption is a function that can be used to configure a [templatedTestProject].
21+
type TemplatedTestProjectOption func(*templatedTestProject)
22+
1723
type templatedTestProject struct {
18-
templateDir string
24+
templateDir string
25+
useTestDefaultConfigs bool
26+
}
27+
28+
// TemplatedUseTestDefaultConfigs configures the templated project to include the test default configs.
29+
// This modifies the project's azldev.toml after copying to add an include directive that
30+
// references the test default configs (which must be copied into the container separately
31+
// using [WithTestDefaultConfigs] on the [ProjectTest]).
32+
func TemplatedUseTestDefaultConfigs() TemplatedTestProjectOption {
33+
return func(p *templatedTestProject) {
34+
p.useTestDefaultConfigs = true
35+
}
1936
}
2037

21-
func NewTemplatedTestProject(t *testing.T, templateDir string) *templatedTestProject {
38+
func NewTemplatedTestProject(
39+
t *testing.T,
40+
templateDir string,
41+
options ...TemplatedTestProjectOption,
42+
) *templatedTestProject {
2243
t.Helper()
2344

2445
// Resolve any relative paths.
@@ -34,18 +55,48 @@ func NewTemplatedTestProject(t *testing.T, templateDir string) *templatedTestPro
3455
templateDir = filepath.Join(dirPath, templateDir)
3556
}
3657

37-
return &templatedTestProject{
58+
project := &templatedTestProject{
3859
templateDir: templateDir,
3960
}
61+
62+
for _, option := range options {
63+
option(project)
64+
}
65+
66+
return project
4067
}
4168

4269
func (p *templatedTestProject) Serialize(t *testing.T, projectDir string) {
4370
t.Helper()
4471

45-
// For now, we just copy the template dir to the destination. This could be
46-
// extended to apply transformations on the input.
47-
err := fileutils.CopyDirRecursive(notDryRun{}, afero.NewOsFs(), p.templateDir, projectDir, fileutils.CopyDirOptions{})
72+
osFS := afero.NewOsFs()
73+
74+
// Copy the template dir to the destination.
75+
err := fileutils.CopyDirRecursive(notDryRun{}, osFS, p.templateDir, projectDir, fileutils.CopyDirOptions{})
4876
require.NoError(t, err)
77+
78+
// If test default configs are requested, modify the azldev.toml to include them.
79+
if p.useTestDefaultConfigs {
80+
configFilePath := filepath.Join(projectDir, projectconfig.DefaultConfigFileName)
81+
82+
// Read and parse the existing config file.
83+
configBytes, err := afero.ReadFile(osFS, configFilePath)
84+
require.NoError(t, err, "failed to read config file for test default configs injection")
85+
86+
var config projectconfig.ConfigFile
87+
require.NoError(t, toml.Unmarshal(configBytes, &config),
88+
"failed to parse config file for test default configs injection")
89+
90+
// Prepend the test default configs include path.
91+
config.Includes = append([]string{TestDefaultConfigsIncludePath}, config.Includes...)
92+
93+
// Write back the modified config file.
94+
modifiedBytes, err := toml.Marshal(config)
95+
require.NoError(t, err, "failed to marshal config file with test default configs")
96+
97+
require.NoError(t, afero.WriteFile(osFS, configFilePath, modifiedBytes, fileperms.PublicFile),
98+
"failed to write config file with test default configs")
99+
}
49100
}
50101

51102
type notDryRun struct{}

0 commit comments

Comments
 (0)