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
9 changes: 9 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ Jump to: [Unreleased](#unreleased) · [3.0.0](#300--2026-10-04) · [2.5.0](#250-
on Google Play. If you'd rather use the PC, **Open in browser instead** under the code still opens the listing
there. (RemEx-2p7um)

### Internal

- **`HostIdentityUnificationTests` now runs on Linux.** `PairingHandlerResponse_UsesHostIdNotInstanceId` and
`QrBootstrapEndpoint_UsesHostIdNotInstanceId` read their source from `Remex.Agent/...`, but the project folder is
`remex.agent`. Windows ignored the case, so they passed there and failed every time on Linux (CachyOS, CI, WSL)
with "Could not locate". They now find the repo root through `[CallerFilePath]`, like the other source-scanning
tests, and build each path from the on-disk case. No other test shared the capitalised path. Checked by swapping
`HostId` for `InstanceId` in both pinned places: both tests went red on Linux, and green again once reverted.

## [3.0.0] — 2026-10-04

**Routines, one look on the PC and the phone, and a new startup splash.** The first release on Google Play
Expand Down
32 changes: 15 additions & 17 deletions remex.agent.tests/HostIdentityUnificationTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Runtime.CompilerServices;

namespace Remex.Agent.Tests;

/// <summary>
Expand Down Expand Up @@ -42,7 +44,7 @@ public void QrBootstrapEndpoint_UsesHostIdNotInstanceId()
{
// Contract test on the source: the /pairing-qr endpoint must return HostBootstrapper.HostId,
// not HostBootstrapper.InstanceId. A regression here re-introduces review-report.md Finding 5.
var bootstrapper = ReadSource("Remex.Agent/HostBootstrapper.cs");
var bootstrapper = ReadSource("remex.agent", "HostBootstrapper.cs");

var qrBlock = ExtractMapGetBlock(bootstrapper, "/pairing-qr");
Assert.Contains("hostId = HostId", qrBlock);
Expand All @@ -56,7 +58,7 @@ public void PairingHandlerResponse_UsesHostIdNotInstanceId()
// Contract test: the PairingHandler's PairingResponse payload must populate HostId
// from HostBootstrapper.HostId (the stable identity), never from InstanceId. The
// review report's Finding 5 specifically called out this asymmetry.
var pairingHandler = ReadSource("Remex.Agent/Handlers/PairingHandler.cs");
var pairingHandler = ReadSource("remex.agent", "Handlers", "PairingHandler.cs");

// Locate the PairingResponse construction and the HostId assignment within it.
var idx = pairingHandler.IndexOf("PairingResponse = new PairingResponse", StringComparison.Ordinal);
Expand All @@ -67,24 +69,20 @@ public void PairingHandlerResponse_UsesHostIdNotInstanceId()
Assert.DoesNotContain("HostId = HostBootstrapper.InstanceId", snippet);
}

private static string ReadSource(string repoRelativePath)
// Segments are the on-disk case from `git ls-files`: the project folder is `remex.agent`, not
// `Remex.Agent`. Windows forgave the old capitalised path; Linux never found the file.
private static string ReadSource(params string[] repoRelativeSegments)
{
// Test runner cwd is the test project's bin directory. Walk up until we find the
// repo root (the directory containing the file path we're asked for).
var dir = new DirectoryInfo(Directory.GetCurrentDirectory());
while (dir is not null)
{
var candidate = Path.Combine(dir.FullName, repoRelativePath);
if (File.Exists(candidate))
{
return File.ReadAllText(candidate);
}
dir = dir.Parent;
}
Assert.Fail($"Could not locate {repoRelativePath} starting from {Directory.GetCurrentDirectory()}.");
return string.Empty; // unreachable
var path = Path.Combine([RepoRoot(), .. repoRelativeSegments]);
Assert.True(File.Exists(path), $"Could not locate {path}.");
return File.ReadAllText(path);
}

// [CallerFilePath] rather than walking up from the assembly, matching the other source-scanning
// tests, so building with --artifacts-path outside the repo does not break this (RemEx-6i1l).
private static string RepoRoot([CallerFilePath] string thisSourceFile = "")
=> Path.GetFullPath(Path.Combine(Path.GetDirectoryName(thisSourceFile)!, ".."));

private static string ExtractMapGetBlock(string source, string route)
{
var sentinel = $"app.MapGet(\"{route}\"";
Expand Down
Loading