@@ -88,6 +88,42 @@ func TestExtractRevisionRefs(t *testing.T) {
8888 {SourceName : "sql-source" , Fields : []string {"file" , "path" }},
8989 },
9090 },
91+ {
92+ name : "http datasource with name" ,
93+ revision : `input.sources["my-data"].http.users.hash` ,
94+ want : []ReferencedSource {
95+ {SourceName : "my-data" , Fields : []string {"http" , "users" , "hash" }},
96+ },
97+ },
98+ {
99+ name : "http datasource with bracket notation" ,
100+ revision : `input.sources["my-data"].http["user-list"].hash` ,
101+ want : []ReferencedSource {
102+ {SourceName : "my-data" , Fields : []string {"http" , "user-list" , "hash" }},
103+ },
104+ },
105+ {
106+ name : "s3 datasource with name" ,
107+ revision : `input.sources["my-data"].s3["model-weights"].hash` ,
108+ want : []ReferencedSource {
109+ {SourceName : "my-data" , Fields : []string {"s3" , "model-weights" , "hash" }},
110+ },
111+ },
112+ {
113+ name : "multiple datasources from same source" ,
114+ revision : `$"{input.sources.data.http.users.hash}-{input.sources.data.http.products.hash}"` ,
115+ want : []ReferencedSource {
116+ {SourceName : "data" , Fields : []string {"http" , "users" , "hash" , "products" }},
117+ },
118+ },
119+ {
120+ name : "mixed git and http datasource" ,
121+ revision : `$"{input.sources.policies.git.commit}-{input.sources.data.http.users.hash}"` ,
122+ want : []ReferencedSource {
123+ {SourceName : "policies" , Fields : []string {"git" , "commit" }},
124+ {SourceName : "data" , Fields : []string {"http" , "users" , "hash" }},
125+ },
126+ },
91127 }
92128
93129 for _ , tt := range tests {
@@ -160,7 +196,7 @@ func TestValidationErrorMessages(t *testing.T) {
160196 wantErrContains : `undefined ref: input.sources.nothere.git.commit` ,
161197 },
162198 {
163- name : "invalid source type - http (schema validation)" ,
199+ name : "invalid source type - http without datasources (schema validation)" ,
164200 revision : `input.sources.policies.http.url` ,
165201 availableSources : []string {"policies" },
166202 wantErrContains : `undefined ref: input.sources.policies.http.url` ,
@@ -172,7 +208,7 @@ func TestValidationErrorMessages(t *testing.T) {
172208 wantErrContains : `undefined ref: input.sources.policies.file.path` ,
173209 },
174210 {
175- name : "invalid source type - s3 (schema validation)" ,
211+ name : "invalid source type - s3 without datasources (schema validation)" ,
176212 revision : `input.sources.policies.s3.bucket` ,
177213 availableSources : []string {"policies" },
178214 wantErrContains : `undefined ref: input.sources.policies.s3.bucket` ,
@@ -200,3 +236,211 @@ func TestValidationErrorMessages(t *testing.T) {
200236 })
201237 }
202238}
239+
240+ func TestValidationErrorMessagesWithDatasources (t * testing.T ) {
241+ tests := []struct {
242+ name string
243+ revision string
244+ sourceMetadata map [string ]map [string ]any
245+ wantErrContains string
246+ }{
247+ {
248+ name : "unknown datasource name under http" ,
249+ revision : `input.sources.data.http.unknown.hash` ,
250+ sourceMetadata : map [string ]map [string ]any {
251+ "data" : {
252+ "http" : map [string ]any {
253+ "users" : map [string ]any {"hash" : "abc123" },
254+ "products" : map [string ]any {"hash" : "def456" },
255+ },
256+ },
257+ },
258+ wantErrContains : `undefined ref: input.sources.data.http.unknown.hash` ,
259+ },
260+ {
261+ name : "unknown datasource name under s3" ,
262+ revision : `input.sources.data.s3.unknown.hash` ,
263+ sourceMetadata : map [string ]map [string ]any {
264+ "data" : {
265+ "s3" : map [string ]any {
266+ "model-weights" : map [string ]any {"hash" : "abc123" },
267+ },
268+ },
269+ },
270+ wantErrContains : `undefined ref: input.sources.data.s3.unknown.hash` ,
271+ },
272+ {
273+ name : "accessing http.hash directly without datasource name" ,
274+ revision : `input.sources.data.http.hash` ,
275+ sourceMetadata : map [string ]map [string ]any {
276+ "data" : {
277+ "http" : map [string ]any {
278+ "users" : map [string ]any {"hash" : "abc123" },
279+ },
280+ },
281+ },
282+ wantErrContains : `undefined ref: input.sources.data.http.hash` ,
283+ },
284+ {
285+ name : "wrong field name on datasource" ,
286+ revision : `input.sources.data.http.users.url` ,
287+ sourceMetadata : map [string ]map [string ]any {
288+ "data" : {
289+ "http" : map [string ]any {
290+ "users" : map [string ]any {"hash" : "abc123" },
291+ },
292+ },
293+ },
294+ wantErrContains : `undefined ref: input.sources.data.http.users.url` ,
295+ },
296+ {
297+ name : "http not in schema when no http datasources exist" ,
298+ revision : `input.sources.policies.http.users.hash` ,
299+ sourceMetadata : map [string ]map [string ]any {
300+ "policies" : {
301+ "git" : map [string ]any {"commit" : "abc123" },
302+ },
303+ },
304+ wantErrContains : `undefined ref: input.sources.policies.http.users.hash` ,
305+ },
306+ }
307+
308+ for _ , tt := range tests {
309+ t .Run (tt .name , func (t * testing.T ) {
310+ _ , err := ResolveRevision (t .Context (), tt .revision , tt .sourceMetadata )
311+ if err == nil {
312+ t .Fatalf ("expected error containing %q, got nil" , tt .wantErrContains )
313+ }
314+
315+ if ! strings .Contains (err .Error (), tt .wantErrContains ) {
316+ t .Errorf ("error message %q does not contain expected substring %q" , err .Error (), tt .wantErrContains )
317+ }
318+ })
319+ }
320+ }
321+
322+ func TestResolveRevision (t * testing.T ) {
323+ tests := []struct {
324+ name string
325+ revision string
326+ sourceMetadata map [string ]map [string ]any
327+ want string
328+ }{
329+ {
330+ name : "empty revision" ,
331+ revision : "" ,
332+ want : "" ,
333+ },
334+ {
335+ name : "static string" ,
336+ revision : `"v1.0.0"` ,
337+ want : "v1.0.0" ,
338+ },
339+ {
340+ name : "git commit" ,
341+ revision : `input.sources.policies.git.commit` ,
342+ sourceMetadata : map [string ]map [string ]any {
343+ "policies" : {
344+ "git" : map [string ]any {"commit" : "abc123def456" },
345+ },
346+ },
347+ want : "abc123def456" ,
348+ },
349+ {
350+ name : "sql hash" ,
351+ revision : `input.sources["sql-source"].sql.hash` ,
352+ sourceMetadata : map [string ]map [string ]any {
353+ "sql-source" : {
354+ "sql" : map [string ]any {"hash" : "deadbeef" },
355+ },
356+ },
357+ want : "deadbeef" ,
358+ },
359+ {
360+ name : "http datasource hash" ,
361+ revision : `input.sources.data.http.users.hash` ,
362+ sourceMetadata : map [string ]map [string ]any {
363+ "data" : {
364+ "http" : map [string ]any {
365+ "users" : map [string ]any {"hash" : "a1b2c3d4" },
366+ },
367+ },
368+ },
369+ want : "a1b2c3d4" ,
370+ },
371+ {
372+ name : "s3 datasource hash" ,
373+ revision : `input.sources.data.s3["model-weights"].hash` ,
374+ sourceMetadata : map [string ]map [string ]any {
375+ "data" : {
376+ "s3" : map [string ]any {
377+ "model-weights" : map [string ]any {"hash" : "s3hash999" },
378+ },
379+ },
380+ },
381+ want : "s3hash999" ,
382+ },
383+ {
384+ name : "template with git commit substring" ,
385+ revision : `$"git-{substring(input.sources.policies.git.commit, 0, 7)}"` ,
386+ sourceMetadata : map [string ]map [string ]any {
387+ "policies" : {
388+ "git" : map [string ]any {"commit" : "abc123def456" },
389+ },
390+ },
391+ want : "git-abc123d" ,
392+ },
393+ {
394+ name : "template combining git and http datasource" ,
395+ revision : `$"{substring(input.sources.policies.git.commit, 0, 7)}-{substring(input.sources.data.http.users.hash, 0, 7)}"` ,
396+ sourceMetadata : map [string ]map [string ]any {
397+ "policies" : {
398+ "git" : map [string ]any {"commit" : "abc123def456" },
399+ },
400+ "data" : {
401+ "http" : map [string ]any {
402+ "users" : map [string ]any {"hash" : "fedcba9876543210" },
403+ },
404+ },
405+ },
406+ want : "abc123d-fedcba9" ,
407+ },
408+ {
409+ name : "template combining two http datasources from same source" ,
410+ revision : `$"{substring(input.sources.data.http.users.hash, 0, 8)}-{substring(input.sources.data.http.products.hash, 0, 8)}"` ,
411+ sourceMetadata : map [string ]map [string ]any {
412+ "data" : {
413+ "http" : map [string ]any {
414+ "users" : map [string ]any {"hash" : "1111111122222222" },
415+ "products" : map [string ]any {"hash" : "3333333344444444" },
416+ },
417+ },
418+ },
419+ want : "11111111-33333333" ,
420+ },
421+ {
422+ name : "http datasource with bracket notation" ,
423+ revision : `input.sources.data.http["user-list"].hash` ,
424+ sourceMetadata : map [string ]map [string ]any {
425+ "data" : {
426+ "http" : map [string ]any {
427+ "user-list" : map [string ]any {"hash" : "bracket-hash" },
428+ },
429+ },
430+ },
431+ want : "bracket-hash" ,
432+ },
433+ }
434+
435+ for _ , tt := range tests {
436+ t .Run (tt .name , func (t * testing.T ) {
437+ got , err := ResolveRevision (t .Context (), tt .revision , tt .sourceMetadata )
438+ if err != nil {
439+ t .Fatalf ("unexpected error: %v" , err )
440+ }
441+ if got != tt .want {
442+ t .Errorf ("got %q, want %q" , got , tt .want )
443+ }
444+ })
445+ }
446+ }
0 commit comments