Skip to content

Commit d645d13

Browse files
committed
feat(validator): enhance mapping validation to include list refs
Updated validation logic to ensure at least one mapping is provided, including support for file and directory list references. Added comprehensive tests to cover various mapping scenarios.
1 parent 9529f02 commit d645d13

2 files changed

Lines changed: 145 additions & 2 deletions

File tree

internal/config/validator.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -506,8 +506,8 @@ func (t *TargetConfig) validateWithLogging(ctx context.Context, logConfig *loggi
506506
default:
507507
}
508508

509-
// Validate that we have at least one file or directory mapping
510-
if len(t.Files) == 0 && len(t.Directories) == 0 {
509+
// Validate that we have at least one file or directory mapping (direct or via list refs)
510+
if len(t.Files) == 0 && len(t.Directories) == 0 && len(t.FileListRefs) == 0 && len(t.DirectoryListRefs) == 0 {
511511
return ErrNoMappings
512512
}
513513

internal/config/validator_test.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,149 @@ func TestTargetConfig_BranchValidation(t *testing.T) {
550550
}
551551
}
552552

553+
// TestTargetConfig_MappingRequirement tests that targets accept any combination of
554+
// direct mappings or list refs, and reject targets with none.
555+
func TestTargetConfig_MappingRequirement(t *testing.T) {
556+
tests := []struct {
557+
name string
558+
target TargetConfig
559+
expectErr bool
560+
}{
561+
{
562+
name: "valid - files only",
563+
target: TargetConfig{
564+
Repo: "org/target",
565+
Files: []FileMapping{{Src: "a.txt", Dest: "a.txt"}},
566+
},
567+
},
568+
{
569+
name: "valid - directories only",
570+
target: TargetConfig{
571+
Repo: "org/target",
572+
Directories: []DirectoryMapping{{Src: "src", Dest: "dest"}},
573+
},
574+
},
575+
{
576+
name: "valid - file list refs only",
577+
target: TargetConfig{
578+
Repo: "org/target",
579+
FileListRefs: []string{"common-files"},
580+
},
581+
},
582+
{
583+
name: "valid - directory list refs only",
584+
target: TargetConfig{
585+
Repo: "org/target",
586+
DirectoryListRefs: []string{"github-workflows"},
587+
},
588+
},
589+
{
590+
name: "valid - file list refs and directory list refs",
591+
target: TargetConfig{
592+
Repo: "org/target",
593+
FileListRefs: []string{"common-files", "editor-config"},
594+
DirectoryListRefs: []string{"github-workflows", "vs-code"},
595+
},
596+
},
597+
{
598+
name: "valid - files and directories",
599+
target: TargetConfig{
600+
Repo: "org/target",
601+
Files: []FileMapping{{Src: "a.txt", Dest: "a.txt"}},
602+
Directories: []DirectoryMapping{{Src: "src", Dest: "dest"}},
603+
},
604+
},
605+
{
606+
name: "valid - all four mapping types",
607+
target: TargetConfig{
608+
Repo: "org/target",
609+
Files: []FileMapping{{Src: "a.txt", Dest: "a.txt"}},
610+
Directories: []DirectoryMapping{{Src: "src", Dest: "dest"}},
611+
FileListRefs: []string{"common-files"},
612+
DirectoryListRefs: []string{"github-workflows"},
613+
},
614+
},
615+
{
616+
name: "invalid - no mappings at all",
617+
target: TargetConfig{
618+
Repo: "org/target",
619+
},
620+
expectErr: true,
621+
},
622+
{
623+
name: "invalid - empty slices for all mapping types",
624+
target: TargetConfig{
625+
Repo: "org/target",
626+
Files: []FileMapping{},
627+
Directories: []DirectoryMapping{},
628+
FileListRefs: []string{},
629+
DirectoryListRefs: []string{},
630+
},
631+
expectErr: true,
632+
},
633+
}
634+
635+
for _, tt := range tests {
636+
t.Run(tt.name, func(t *testing.T) {
637+
ctx := context.Background()
638+
logger := logrus.NewEntry(logrus.StandardLogger())
639+
640+
err := tt.target.validateWithLogging(ctx, nil, logger)
641+
642+
if tt.expectErr {
643+
require.Error(t, err)
644+
assert.ErrorIs(t, err, ErrNoMappings)
645+
} else {
646+
assert.NoError(t, err)
647+
}
648+
})
649+
}
650+
}
651+
652+
// TestTargetConfig_ListRefsOnlyFullValidation verifies that a target with only list refs
653+
// passes full config-level validation (the real-world scenario that was broken).
654+
func TestTargetConfig_ListRefsOnlyFullValidation(t *testing.T) {
655+
config := &Config{
656+
Version: 1,
657+
FileLists: []FileList{
658+
{
659+
ID: "common-files",
660+
Name: "Common Files",
661+
Files: []FileMapping{
662+
{Src: ".editorconfig", Dest: ".editorconfig"},
663+
},
664+
},
665+
},
666+
DirectoryLists: []DirectoryList{
667+
{
668+
ID: "github-workflows",
669+
Name: "GitHub Workflows",
670+
Directories: []DirectoryMapping{
671+
{Src: ".github/workflows", Dest: ".github/workflows"},
672+
},
673+
},
674+
},
675+
Groups: []Group{
676+
{
677+
Name: "test-group",
678+
ID: "test-group",
679+
Source: SourceConfig{Repo: "org/source", Branch: "main"},
680+
Targets: []TargetConfig{
681+
{
682+
Repo: "org/target",
683+
FileListRefs: []string{"common-files"},
684+
DirectoryListRefs: []string{"github-workflows"},
685+
Transform: Transform{RepoName: true},
686+
},
687+
},
688+
},
689+
},
690+
}
691+
692+
err := config.Validate()
693+
assert.NoError(t, err)
694+
}
695+
553696
// TestValidate_DuplicateFileDestinations verifies that duplicate file destinations
554697
// within the same target are detected
555698
func TestValidate_DuplicateFileDestinations(t *testing.T) {

0 commit comments

Comments
 (0)