feat(config): add merge_queue section to rig config schema
Add MergeQueueConfig struct and RigConfig type with: - All merge queue settings (enabled, target_branch, on_conflict, etc.) - Default values via DefaultMergeQueueConfig() - Validation for on_conflict strategy and poll_interval duration - Load/Save/Validate functions following existing config patterns - Comprehensive tests for round-trip, custom config, and validation Implements gt-h5n.8. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -130,3 +130,196 @@ func TestValidationErrors(t *testing.T) {
|
||||
t.Error("expected error for missing role")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRigConfigRoundTrip(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "config.json")
|
||||
|
||||
original := NewRigConfig()
|
||||
|
||||
if err := SaveRigConfig(path, original); err != nil {
|
||||
t.Fatalf("SaveRigConfig: %v", err)
|
||||
}
|
||||
|
||||
loaded, err := LoadRigConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadRigConfig: %v", err)
|
||||
}
|
||||
|
||||
if loaded.Type != "rig" {
|
||||
t.Errorf("Type = %q, want 'rig'", loaded.Type)
|
||||
}
|
||||
if loaded.Version != CurrentRigConfigVersion {
|
||||
t.Errorf("Version = %d, want %d", loaded.Version, CurrentRigConfigVersion)
|
||||
}
|
||||
if loaded.MergeQueue == nil {
|
||||
t.Fatal("MergeQueue is nil")
|
||||
}
|
||||
if !loaded.MergeQueue.Enabled {
|
||||
t.Error("MergeQueue.Enabled = false, want true")
|
||||
}
|
||||
if loaded.MergeQueue.TargetBranch != "main" {
|
||||
t.Errorf("MergeQueue.TargetBranch = %q, want 'main'", loaded.MergeQueue.TargetBranch)
|
||||
}
|
||||
if loaded.MergeQueue.OnConflict != OnConflictAssignBack {
|
||||
t.Errorf("MergeQueue.OnConflict = %q, want %q", loaded.MergeQueue.OnConflict, OnConflictAssignBack)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRigConfigWithCustomMergeQueue(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "config.json")
|
||||
|
||||
original := &RigConfig{
|
||||
Type: "rig",
|
||||
Version: 1,
|
||||
MergeQueue: &MergeQueueConfig{
|
||||
Enabled: true,
|
||||
TargetBranch: "develop",
|
||||
IntegrationBranches: false,
|
||||
OnConflict: OnConflictAutoRebase,
|
||||
RunTests: true,
|
||||
TestCommand: "make test",
|
||||
DeleteMergedBranches: false,
|
||||
RetryFlakyTests: 3,
|
||||
PollInterval: "1m",
|
||||
MaxConcurrent: 2,
|
||||
},
|
||||
}
|
||||
|
||||
if err := SaveRigConfig(path, original); err != nil {
|
||||
t.Fatalf("SaveRigConfig: %v", err)
|
||||
}
|
||||
|
||||
loaded, err := LoadRigConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadRigConfig: %v", err)
|
||||
}
|
||||
|
||||
mq := loaded.MergeQueue
|
||||
if mq.TargetBranch != "develop" {
|
||||
t.Errorf("TargetBranch = %q, want 'develop'", mq.TargetBranch)
|
||||
}
|
||||
if mq.OnConflict != OnConflictAutoRebase {
|
||||
t.Errorf("OnConflict = %q, want %q", mq.OnConflict, OnConflictAutoRebase)
|
||||
}
|
||||
if mq.TestCommand != "make test" {
|
||||
t.Errorf("TestCommand = %q, want 'make test'", mq.TestCommand)
|
||||
}
|
||||
if mq.RetryFlakyTests != 3 {
|
||||
t.Errorf("RetryFlakyTests = %d, want 3", mq.RetryFlakyTests)
|
||||
}
|
||||
if mq.PollInterval != "1m" {
|
||||
t.Errorf("PollInterval = %q, want '1m'", mq.PollInterval)
|
||||
}
|
||||
if mq.MaxConcurrent != 2 {
|
||||
t.Errorf("MaxConcurrent = %d, want 2", mq.MaxConcurrent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRigConfigValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config *RigConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid config",
|
||||
config: &RigConfig{
|
||||
Type: "rig",
|
||||
Version: 1,
|
||||
MergeQueue: DefaultMergeQueueConfig(),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid config without merge queue",
|
||||
config: &RigConfig{
|
||||
Type: "rig",
|
||||
Version: 1,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "wrong type",
|
||||
config: &RigConfig{
|
||||
Type: "wrong",
|
||||
Version: 1,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid on_conflict",
|
||||
config: &RigConfig{
|
||||
Type: "rig",
|
||||
Version: 1,
|
||||
MergeQueue: &MergeQueueConfig{
|
||||
OnConflict: "invalid",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid poll_interval",
|
||||
config: &RigConfig{
|
||||
Type: "rig",
|
||||
Version: 1,
|
||||
MergeQueue: &MergeQueueConfig{
|
||||
PollInterval: "not-a-duration",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validateRigConfig(tt.config)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("validateRigConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultMergeQueueConfig(t *testing.T) {
|
||||
cfg := DefaultMergeQueueConfig()
|
||||
|
||||
if !cfg.Enabled {
|
||||
t.Error("Enabled should be true by default")
|
||||
}
|
||||
if cfg.TargetBranch != "main" {
|
||||
t.Errorf("TargetBranch = %q, want 'main'", cfg.TargetBranch)
|
||||
}
|
||||
if !cfg.IntegrationBranches {
|
||||
t.Error("IntegrationBranches should be true by default")
|
||||
}
|
||||
if cfg.OnConflict != OnConflictAssignBack {
|
||||
t.Errorf("OnConflict = %q, want %q", cfg.OnConflict, OnConflictAssignBack)
|
||||
}
|
||||
if !cfg.RunTests {
|
||||
t.Error("RunTests should be true by default")
|
||||
}
|
||||
if cfg.TestCommand != "go test ./..." {
|
||||
t.Errorf("TestCommand = %q, want 'go test ./...'", cfg.TestCommand)
|
||||
}
|
||||
if !cfg.DeleteMergedBranches {
|
||||
t.Error("DeleteMergedBranches should be true by default")
|
||||
}
|
||||
if cfg.RetryFlakyTests != 1 {
|
||||
t.Errorf("RetryFlakyTests = %d, want 1", cfg.RetryFlakyTests)
|
||||
}
|
||||
if cfg.PollInterval != "30s" {
|
||||
t.Errorf("PollInterval = %q, want '30s'", cfg.PollInterval)
|
||||
}
|
||||
if cfg.MaxConcurrent != 1 {
|
||||
t.Errorf("MaxConcurrent = %d, want 1", cfg.MaxConcurrent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRigConfigNotFound(t *testing.T) {
|
||||
_, err := LoadRigConfig("/nonexistent/path.json")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for nonexistent file")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user