(t *testing.T)
| 183 | } |
| 184 | |
| 185 | func TestEngine_ConflictOnLocalChange(t *testing.T) { |
| 186 | sourceName := "test-conflict" |
| 187 | defer cleanupRegistry(sourceName) |
| 188 | |
| 189 | setupMockSource(sourceName, []ExternalTask{ |
| 190 | {ExternalID: "EXT-1", Title: "A task", Status: "open"}, |
| 191 | }) |
| 192 | |
| 193 | dir := t.TempDir() |
| 194 | outputDir := filepath.Join(dir, "tasks") |
| 195 | |
| 196 | engine := &Engine{ConfigDir: dir} |
| 197 | srcCfg := SourceConfig{ |
| 198 | Name: sourceName, |
| 199 | OutputDir: outputDir, |
| 200 | FieldMap: FieldMap{Status: map[string]string{"open": "pending"}}, |
| 201 | } |
| 202 | |
| 203 | // First sync creates the file |
| 204 | result1, err := engine.RunSync(srcCfg) |
| 205 | if err != nil { |
| 206 | t.Fatalf("first sync error: %v", err) |
| 207 | } |
| 208 | if len(result1.Created) != 1 { |
| 209 | t.Fatalf("expected 1 created, got %d", len(result1.Created)) |
| 210 | } |
| 211 | |
| 212 | // Modify the local file |
| 213 | filePath := result1.Created[0].FilePath |
| 214 | data, err := os.ReadFile(filePath) |
| 215 | if err != nil { |
| 216 | t.Fatalf("failed to read file: %v", err) |
| 217 | } |
| 218 | modified := strings.Replace(string(data), "pending", "in-progress", 1) |
| 219 | if err := os.WriteFile(filePath, []byte(modified), 0644); err != nil { |
| 220 | t.Fatalf("failed to modify file: %v", err) |
| 221 | } |
| 222 | |
| 223 | // Second sync — should detect conflict |
| 224 | result2, err := engine.RunSync(srcCfg) |
| 225 | if err != nil { |
| 226 | t.Fatalf("second sync error: %v", err) |
| 227 | } |
| 228 | if len(result2.Conflicts) != 1 { |
| 229 | t.Errorf("expected 1 conflict, got %d (created=%d, updated=%d, skipped=%d)", |
| 230 | len(result2.Conflicts), len(result2.Created), len(result2.Updated), len(result2.Skipped)) |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | func TestEngine_ConflictRemote(t *testing.T) { |
| 235 | sourceName := "test-conflict-remote" |
nothing calls this directly
no test coverage detected