Skip to content

Commit 48fd872

Browse files
committed
test(cli): add type casing tests for input types
Tests add+transfer for all input types (file, dir, utf8, helm) in both lowercase and UpperCamelCase forms. Helm chart is created inline in t.TempDir() to avoid cross-module testdata references. Note: UpperCamelCase tests (File/v1, Dir/v1, UTF8/v1, Helm/v1) will pass once the bindings changes from PR open-component-model#2057 are merged and cli/go.mod is updated to reference the new bindings versions. On-behalf-of: Gerald Morrison (SAP) <gerald.morrison@sap.com> Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
1 parent 6b5d591 commit 48fd872

File tree

1 file changed

+341
-0
lines changed

1 file changed

+341
-0
lines changed

cli/cmd/type_casing_test.go

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
package cmd_test
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
"testing"
10+
11+
"github.com/stretchr/testify/require"
12+
13+
"ocm.software/open-component-model/bindings/go/blob"
14+
"ocm.software/open-component-model/bindings/go/blob/filesystem"
15+
"ocm.software/open-component-model/bindings/go/ctf"
16+
"ocm.software/open-component-model/bindings/go/oci"
17+
ocictf "ocm.software/open-component-model/bindings/go/oci/ctf"
18+
"ocm.software/open-component-model/cli/cmd/internal/test"
19+
)
20+
21+
// createMinimalHelmChart creates a bare Helm chart directory in tmp/mychart
22+
// containing only the minimum files required for packaging (Chart.yaml + one template).
23+
func createMinimalHelmChart(t *testing.T, tmp string) string {
24+
t.Helper()
25+
r := require.New(t)
26+
27+
chartDir := filepath.Join(tmp, "mychart")
28+
r.NoError(os.MkdirAll(filepath.Join(chartDir, "templates"), 0o755))
29+
r.NoError(os.WriteFile(filepath.Join(chartDir, "Chart.yaml"),
30+
[]byte("name: mychart\nversion: 0.1.0\n"), 0o600))
31+
r.NoError(os.WriteFile(filepath.Join(chartDir, "templates", "pod.yaml"),
32+
[]byte("apiVersion: v1\nkind: Pod\nmetadata:\n name: test\nspec:\n containers:\n - image: busybox\n name: test\n"), 0o600))
33+
34+
return chartDir
35+
}
36+
37+
func Test_Add_And_Transfer_Input_Type_Casing(t *testing.T) {
38+
tests := []struct {
39+
name string
40+
inputType string
41+
resourceType string
42+
resourceName string
43+
expectedAccess string
44+
setupConstructor func(t *testing.T, tmp string) string
45+
verifyContent func(t *testing.T, content []byte)
46+
skipBlobCompare bool
47+
}{
48+
{
49+
name: "file/v1 lowercase",
50+
inputType: "file/v1",
51+
resourceType: "blob",
52+
resourceName: "my-file",
53+
expectedAccess: "localBlob/v1",
54+
setupConstructor: func(t *testing.T, tmp string) string {
55+
filePath := filepath.Join(tmp, "test-file.txt")
56+
require.NoError(t, os.WriteFile(filePath, []byte("file-content"), 0o600))
57+
return fmt.Sprintf(`
58+
name: ocm.software/test-type-casing
59+
version: 1.0.0
60+
provider:
61+
name: ocm.software
62+
resources:
63+
- name: my-file
64+
type: blob
65+
input:
66+
type: file/v1
67+
path: %s
68+
`, filePath)
69+
},
70+
verifyContent: func(t *testing.T, content []byte) {
71+
require.Equal(t, "file-content", string(content))
72+
},
73+
},
74+
{
75+
name: "File/v1 upper camel case",
76+
inputType: "File/v1",
77+
resourceType: "blob",
78+
resourceName: "my-file",
79+
expectedAccess: "localBlob/v1",
80+
setupConstructor: func(t *testing.T, tmp string) string {
81+
filePath := filepath.Join(tmp, "test-file.txt")
82+
require.NoError(t, os.WriteFile(filePath, []byte("file-content-upper"), 0o600))
83+
return fmt.Sprintf(`
84+
name: ocm.software/test-type-casing
85+
version: 1.0.0
86+
provider:
87+
name: ocm.software
88+
resources:
89+
- name: my-file
90+
type: blob
91+
input:
92+
type: File/v1
93+
path: %s
94+
`, filePath)
95+
},
96+
verifyContent: func(t *testing.T, content []byte) {
97+
require.Equal(t, "file-content-upper", string(content))
98+
},
99+
},
100+
{
101+
name: "dir/v1 lowercase",
102+
inputType: "dir/v1",
103+
resourceType: "blob",
104+
resourceName: "my-dir",
105+
expectedAccess: "localBlob/v1",
106+
setupConstructor: func(t *testing.T, tmp string) string {
107+
dirPath := filepath.Join(tmp, "test-dir")
108+
require.NoError(t, os.MkdirAll(dirPath, 0o755))
109+
require.NoError(t, os.WriteFile(filepath.Join(dirPath, "data.txt"), []byte("dir-content"), 0o600))
110+
return fmt.Sprintf(`
111+
name: ocm.software/test-type-casing
112+
version: 1.0.0
113+
provider:
114+
name: ocm.software
115+
resources:
116+
- name: my-dir
117+
type: blob
118+
input:
119+
type: dir/v1
120+
path: %s
121+
`, dirPath)
122+
},
123+
verifyContent: func(t *testing.T, content []byte) {
124+
require.NotEmpty(t, content, "dir content should not be empty (tar archive)")
125+
},
126+
},
127+
{
128+
name: "Dir/v1 upper camel case",
129+
inputType: "Dir/v1",
130+
resourceType: "blob",
131+
resourceName: "my-dir",
132+
expectedAccess: "localBlob/v1",
133+
setupConstructor: func(t *testing.T, tmp string) string {
134+
dirPath := filepath.Join(tmp, "test-dir")
135+
require.NoError(t, os.MkdirAll(dirPath, 0o755))
136+
require.NoError(t, os.WriteFile(filepath.Join(dirPath, "data.txt"), []byte("dir-content-upper"), 0o600))
137+
return fmt.Sprintf(`
138+
name: ocm.software/test-type-casing
139+
version: 1.0.0
140+
provider:
141+
name: ocm.software
142+
resources:
143+
- name: my-dir
144+
type: blob
145+
input:
146+
type: Dir/v1
147+
path: %s
148+
`, dirPath)
149+
},
150+
verifyContent: func(t *testing.T, content []byte) {
151+
require.NotEmpty(t, content, "dir content should not be empty (tar archive)")
152+
},
153+
},
154+
{
155+
name: "utf8/v1 lowercase",
156+
inputType: "utf8/v1",
157+
resourceType: "blob",
158+
resourceName: "my-text",
159+
expectedAccess: "localBlob/v1",
160+
setupConstructor: func(t *testing.T, _ string) string {
161+
return `
162+
name: ocm.software/test-type-casing
163+
version: 1.0.0
164+
provider:
165+
name: ocm.software
166+
resources:
167+
- name: my-text
168+
type: blob
169+
input:
170+
type: utf8/v1
171+
text: "hello from utf8 lowercase"
172+
`
173+
},
174+
verifyContent: func(t *testing.T, content []byte) {
175+
require.Equal(t, "hello from utf8 lowercase", string(content))
176+
},
177+
},
178+
{
179+
name: "UTF8/v1 upper camel case",
180+
inputType: "UTF8/v1",
181+
resourceType: "blob",
182+
resourceName: "my-text",
183+
expectedAccess: "localBlob/v1",
184+
setupConstructor: func(t *testing.T, _ string) string {
185+
return `
186+
name: ocm.software/test-type-casing
187+
version: 1.0.0
188+
provider:
189+
name: ocm.software
190+
resources:
191+
- name: my-text
192+
type: blob
193+
input:
194+
type: UTF8/v1
195+
text: "hello from UTF8 upper"
196+
`
197+
},
198+
verifyContent: func(t *testing.T, content []byte) {
199+
require.Equal(t, "hello from UTF8 upper", string(content))
200+
},
201+
},
202+
{
203+
name: "helm/v1 input lowercase",
204+
inputType: "helm/v1",
205+
resourceType: "helmChart",
206+
resourceName: "my-chart",
207+
expectedAccess: "localBlob/v1",
208+
skipBlobCompare: true,
209+
setupConstructor: func(t *testing.T, tmp string) string {
210+
chartDir := createMinimalHelmChart(t, tmp)
211+
return fmt.Sprintf(`
212+
name: ocm.software/test-type-casing
213+
version: 1.0.0
214+
provider:
215+
name: ocm.software
216+
resources:
217+
- name: my-chart
218+
type: helmChart
219+
input:
220+
type: helm/v1
221+
path: %s
222+
`, chartDir)
223+
},
224+
verifyContent: func(t *testing.T, content []byte) {
225+
require.NotEmpty(t, content, "helm chart content should not be empty")
226+
},
227+
},
228+
{
229+
name: "Helm/v1 input upper camel case",
230+
inputType: "Helm/v1",
231+
resourceType: "helmChart",
232+
resourceName: "my-chart",
233+
expectedAccess: "localBlob/v1",
234+
skipBlobCompare: true,
235+
setupConstructor: func(t *testing.T, tmp string) string {
236+
chartDir := createMinimalHelmChart(t, tmp)
237+
return fmt.Sprintf(`
238+
name: ocm.software/test-type-casing
239+
version: 1.0.0
240+
provider:
241+
name: ocm.software
242+
resources:
243+
- name: my-chart
244+
type: helmChart
245+
input:
246+
type: Helm/v1
247+
path: %s
248+
`, chartDir)
249+
},
250+
verifyContent: func(t *testing.T, content []byte) {
251+
require.NotEmpty(t, content, "helm chart content should not be empty")
252+
},
253+
},
254+
}
255+
256+
for _, tc := range tests {
257+
t.Run(tc.name, func(t *testing.T) {
258+
r := require.New(t)
259+
tmp := t.TempDir()
260+
261+
constructorYAML := tc.setupConstructor(t, tmp)
262+
constructorPath := filepath.Join(tmp, "constructor.yaml")
263+
r.NoError(os.WriteFile(constructorPath, []byte(constructorYAML), 0o600))
264+
265+
// Step 1: add cv
266+
sourceArchive := filepath.Join(tmp, "source-archive")
267+
logs := test.NewJSONLogReader()
268+
_, err := test.OCM(t, test.WithArgs("add", "cv",
269+
"--constructor", constructorPath,
270+
"--repository", sourceArchive,
271+
), test.WithErrorOutput(logs))
272+
r.NoError(err, "add cv failed for input type %s", tc.inputType)
273+
274+
// Verify source CTF
275+
sourceFS, err := filesystem.NewFS(sourceArchive, os.O_RDONLY)
276+
r.NoError(err)
277+
sourceRepo, err := oci.NewRepository(ocictf.WithCTF(ocictf.NewFromCTF(ctf.NewFileSystemCTF(sourceFS))))
278+
r.NoError(err)
279+
280+
desc, err := sourceRepo.GetComponentVersion(t.Context(), "ocm.software/test-type-casing", "1.0.0")
281+
r.NoError(err, "could not get component version from source")
282+
r.Len(desc.Component.Resources, 1)
283+
r.Equal(tc.resourceName, desc.Component.Resources[0].Name)
284+
r.Equal(tc.resourceType, desc.Component.Resources[0].Type)
285+
r.Equal(tc.expectedAccess, desc.Component.Resources[0].Access.GetType().String())
286+
287+
// Verify content from source
288+
sourceBlob, _, err := sourceRepo.GetLocalResource(t.Context(), "ocm.software/test-type-casing", "1.0.0", desc.Component.Resources[0].ToIdentity())
289+
r.NoError(err, "could not get local resource from source")
290+
var sourceBuf bytes.Buffer
291+
r.NoError(blob.Copy(&sourceBuf, sourceBlob))
292+
tc.verifyContent(t, sourceBuf.Bytes())
293+
294+
// Step 2: transfer cv
295+
targetArchive := filepath.Join(tmp, "target-archive")
296+
sourceRef := fmt.Sprintf("ctf::%s//ocm.software/test-type-casing:1.0.0", sourceArchive)
297+
targetRef := fmt.Sprintf("ctf::%s", targetArchive)
298+
299+
transferLogs := test.NewJSONLogReader()
300+
_, err = test.OCM(t, test.WithArgs("transfer", "component-version", sourceRef, targetRef),
301+
test.WithErrorOutput(transferLogs))
302+
r.NoError(err, "transfer cv failed for input type %s", tc.inputType)
303+
304+
// Verify transfer logs contain success
305+
transferEntries, err := transferLogs.List()
306+
r.NoError(err)
307+
found := false
308+
for _, entry := range transferEntries {
309+
if strings.Contains(fmt.Sprint(entry), "transfer completed successfully") {
310+
found = true
311+
break
312+
}
313+
}
314+
r.True(found, "expected transfer success log for input type %s", tc.inputType)
315+
316+
// Verify target CTF
317+
targetFS, err := filesystem.NewFS(targetArchive, os.O_RDONLY)
318+
r.NoError(err)
319+
targetRepo, err := oci.NewRepository(ocictf.WithCTF(ocictf.NewFromCTF(ctf.NewFileSystemCTF(targetFS))))
320+
r.NoError(err)
321+
322+
targetDesc, err := targetRepo.GetComponentVersion(t.Context(), "ocm.software/test-type-casing", "1.0.0")
323+
r.NoError(err, "could not get component version from target")
324+
r.Len(targetDesc.Component.Resources, 1)
325+
r.Equal(tc.resourceName, targetDesc.Component.Resources[0].Name)
326+
r.Equal(tc.resourceType, targetDesc.Component.Resources[0].Type)
327+
r.Equal(tc.expectedAccess, targetDesc.Component.Resources[0].Access.GetType().String())
328+
329+
// Verify content from target matches source
330+
targetBlob, _, err := targetRepo.GetLocalResource(t.Context(), "ocm.software/test-type-casing", "1.0.0", targetDesc.Component.Resources[0].ToIdentity())
331+
r.NoError(err, "could not get local resource from target")
332+
var targetBuf bytes.Buffer
333+
r.NoError(blob.Copy(&targetBuf, targetBlob))
334+
if tc.skipBlobCompare {
335+
r.NotEmpty(targetBuf.Bytes(), "target blob should not be empty for type %s", tc.inputType)
336+
} else {
337+
r.Equal(sourceBuf.Bytes(), targetBuf.Bytes(), "transferred content should match source for type %s", tc.inputType)
338+
}
339+
})
340+
}
341+
}

0 commit comments

Comments
 (0)