Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions RackPeek.Domain/UseCases/RenameResourceUseCase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,20 @@ public async Task ExecuteAsync(string originalName, string newName) {
newName = Normalize.HardwareName(newName);
ThrowIfInvalid.ResourceName(newName);

Resource? existingResource = await repo.GetByNameAsync(newName);
Resource? original = await repo.GetByNameAsync(originalName);

IReadOnlyList<Resource> resources = await repo.GetAllOfTypeAsync<Resource>();
Resource? existingResource = resources.FirstOrDefault(r =>
!ReferenceEquals(r, original) && r.Name.Equals(newName, StringComparison.OrdinalIgnoreCase));
if (existingResource != null)
throw new ConflictException($"{existingResource.Kind} resource '{newName}' already exists.");

Resource? original = await repo.GetByNameAsync(originalName);
if (original == null)
throw new NotFoundException($"Resource '{originalName}' not found.");

if (original.Name == newName)
return;

original.Name = newName;
await repo.UpdateAsync(original);

Expand Down
106 changes: 106 additions & 0 deletions Tests/EndToEnd/ConnectionTests/RenameResourceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,110 @@ await ExecuteAsync("connections", "add",
Assert.Contains("srv-prod-app-01", yaml);
Assert.Contains("app-backend-link", yaml);
}

[Fact]
public async Task rename_that_only_changes_case_updates_the_name() {
await ExecuteAsync("servers", "add", "CaseOnly21");

(var output, var yaml) = await ExecuteAsync("servers", "rename", "CaseOnly21", "caseonly21");

Assert.Contains("Server 'CaseOnly21' renamed to 'caseonly21'.", output);
Assert.Contains("name: caseonly21", yaml);
Assert.DoesNotContain("name: CaseOnly21", yaml);
}

[Fact]
public async Task rename_that_only_changes_case_updates_runs_on_and_connections() {
await ExecuteAsync("servers", "add", "CaseOnly31");
await ExecuteAsync("servers", "add", "CaseOnly32");

await ExecuteAsync("servers", "nic", "add", "CaseOnly31",
"--type", "RJ45", "--speed", "10", "--ports", "2");

await ExecuteAsync("servers", "nic", "add", "CaseOnly32",
"--type", "RJ45", "--speed", "10", "--ports", "2");

await ExecuteAsync("connections", "add",
"CaseOnly31", "0", "0",
"CaseOnly32", "0", "0",
"--label", "case-only-link");

await ExecuteAsync("systems", "add", "sys-case-only-31");
await ExecuteAsync("systems", "set", "sys-case-only-31", "--runs-on", "CaseOnly31");

(var output, var yaml) = await ExecuteAsync("servers", "rename", "CaseOnly31", "CASEONLY31");

Assert.Contains("Server 'CaseOnly31' renamed to 'CASEONLY31'.", output);
Assert.Contains("name: CASEONLY31", yaml);
Assert.Contains("resource: CASEONLY31", yaml);
Assert.Contains("- CASEONLY31", yaml);
Assert.DoesNotContain("CaseOnly31", yaml);
}

[Fact]
public async Task rename_onto_another_resources_case_variant_is_still_refused() {
await ExecuteAsync("servers", "add", "Clash41");
await ExecuteAsync("servers", "add", "Clash42");
await ExecuteAsync("systems", "add", "clash43");

(var sameKind, _) = await ExecuteAsync("servers", "rename", "Clash41", "CLASH42");
(var otherKind, var yaml) = await ExecuteAsync("servers", "rename", "Clash41", "CLASH43");

Assert.Contains("Conflict: Server resource 'CLASH42' already exists.", sameKind);
Assert.Contains("Conflict: System resource 'CLASH43' already exists.", otherKind);
Assert.Contains("name: Clash41", yaml);
Assert.Contains("name: Clash42", yaml);
Assert.Contains("name: clash43", yaml);
Assert.DoesNotContain("CLASH4", yaml);
}

[Fact]
public async Task rename_to_the_identical_name_leaves_the_file_unchanged() {
await ExecuteAsync("servers", "add", "Same51");
await ExecuteAsync("servers", "add", "Same52");
await ExecuteAsync("servers", "add", "Same53");

foreach (var s in new[] { "Same51", "Same52", "Same53" }) {
await ExecuteAsync("servers", "nic", "add", s,
"--type", "RJ45", "--speed", "10", "--ports", "2");
}

await ExecuteAsync("connections", "add",
"Same51", "0", "0",
"Same52", "0", "0",
"--label", "same-link");

await ExecuteAsync("connections", "add",
"Same52", "0", "1",
"Same53", "0", "0",
"--label", "other-link");

await ExecuteAsync("systems", "add", "sys-same-51");
(_, var before) = await ExecuteAsync("systems", "set", "sys-same-51", "--runs-on", "same51");

(var output, var after) = await ExecuteAsync("servers", "rename", "Same51", "Same51");

Assert.Contains("Server 'Same51' renamed to 'Same51'.", output);
Assert.Equal(before, after);
}

[Fact]
public async Task rename_is_refused_when_the_config_already_holds_a_case_variant() {
await File.WriteAllTextAsync(Path.Combine(fs.Root, "config.yaml"), """
version: 4
resources:
- kind: Server
name: Dup61
- kind: Server
name: dup61
connections: []
""");

(var output, var yaml) = await ExecuteAsync("servers", "rename", "Dup61", "DUP61");

Assert.Contains("Conflict: Server resource 'DUP61' already exists.", output);
Assert.Contains("name: Dup61", yaml);
Assert.Contains("name: dup61", yaml);
Assert.DoesNotContain("DUP61", yaml);
}
}
Loading