# p4api.net-unit-test/ — testing approach

Scoped instructions for the C# unit test suite. Loads when you read/edit files under `p4api.net/p4api.net-unit-test/`.

Project overview: root `CLAUDE.md`. Test execution recipe: `.claude/skills/test/SKILL.md`. C++ bridge tests: see `../p4bridge-unit-test/CLAUDE.md`.

## Framework & layout

- **MSTest** (`[TestClass]` / `[TestMethod]`) with **NLog** for diagnostics (config in `NLog.config`).
- One `*Test.cs` file per public class in `../p4api.net/`: `Client.cs` → `ClientTest.cs`, `Changelist.cs` → `ChangelistTest.cs`, etc.
- Bootstrap in `MainTest.cs` — assembly-level setup/teardown, NLog initialization, p4d process cleanup.
- Helpers in `Utilities.cs` — `DeployP4Server`, `SetClientRoot`, `TestClientRoot`, command-string builders.

## Fixture configuration

- **`appsettings.json`** holds the per-OS p4d path, test directory, server port:
  - `LinuxP4dPath`, `LinuxArm64P4dPath`, `OsxP4dPath`, `WindowsP4dPath` — selected by `Utilities.cs` based on the running OS.
  - The committed default is the **Jenkins-host layout** (`/home/mount/p4-bin/...`) and usually doesn't exist on dev/agent machines.
- **The config loader (`Utilities.cs` `UnitTestSettings.GetIConfigurationRoot`) reads `appsettings.json` from the *source* tree** — `SetBasePath(GetUnitTestDir())`, where `GetUnitTestDir()` is the directory of `Utilities.cs` captured via `[CallerFilePath]`. It uses only `AddJsonFile` (no `AddEnvironmentVariables`), so you **cannot** override the p4d path via env vars, and **patching the `bin/Release/net10.0/appsettings.json` runtime copy has no effect** — that copy is never read. Two options when the Jenkins path doesn't exist:
  1. **Preferred — symlink** (touches no tracked file): `mkdir -p <expected> && ln -sf <real p4d> <expected>/p4d`
  2. Edit the p4d field in the **source** `appsettings.json` — but treat it as a **local-only** change: revert before submitting, never leave it in a changelist.

See `.claude/skills/test/SKILL.md` step 1 for the full resolution procedure.

## Test categories

- **Per-type tests** — `<Type>Test.cs` for each public type in `../p4api.net/`. Covers CRUD, serialization round-trips, edge cases.
- **Connection / Server** — `ConnectionTest.cs`, `P4ServerMultiThreadingTest.cs`.
- **Command dispatch** — `P4CommandTest.cs`.
- **Forms** — `FormTest.cs`, `FormSpecTest.cs`.
- **Exception** — `P4ExceptionTest.cs`.
- **Map API** — `P4MapApiTest.cs`.

## Patterns (where tests usually break)

1. **`*EnviroTest` cases read ambient env vars** — `$P4USER`, `$P4PORT`, `$P4CLIENT`, `$P4PASSWD`, etc. Unset them before running, or tests fail with `Expected:<admin>. Actual:<your-user>` mismatches. The `/test` skill step 1 does this; if you're invoking `dotnet test` directly, unset them yourself.
2. **`Utilities.DeployP4Server()` invokes the configured `P4dPath`.** If that path doesn't exist (most dev/agent containers), every test that calls it fails with a meaningless error before any assertion. Fix the p4d path first — see fixture configuration above.
3. **`Thread.Sleep` is sometimes intentional** for p4d-startup timing. Don't blindly replace with retry/poll without understanding what's being awaited.
4. **CI flake-exclusion filter** lives in `../build_tools/docker/Linux/p4api.net-ubuntu-net8-net10/files/02-build-p4apinet.sh`. Don't silence a new failure by adding it without owner approval — the `/review` skill flags this as a blocker.
5. **Tests that connect through SSL** use `localhost:6666` with a known fingerprint; the trust dialog must be pre-handled.

## Coverage expectations

- Each new public method or behavior change → matching `[TestMethod]`.
- The test must **pass with the change and fail without it**. If it'd still pass on unchanged main, it's not exercising the new behavior.
- For exception paths: `[ExpectedException(typeof(P4Exception))]` or `Assert.ThrowsException<>` for finer-grained matching.
- For PInvoke-touching changes: **also** add a `../p4bridge-unit-test/` case. The native harness catches ABI breaks the managed tests can't.

## Naming & style

- Test method: `<Behavior>Test` or `<Behavior>_<Scenario>Test` (e.g. `ConnectBadCredTest`, `ConnectUsingP4TestHostEnviroTest`).
- File: `<TypeUnderTest>Test.cs`.
- Match the surrounding file's style; no project-wide formatter.

## Running

See `.claude/skills/test/SKILL.md`. In short:

```
dotnet test p4apicore.net.sln -c Release -f net10.0 \
    --logger "trx;verbosity=minimal" --logger "console;verbosity=minimal" \
    --filter "<filter>"
```

`-c Release` is mandatory — without it the runner defaults to Debug and can't locate `libp4bridge.*`. Results land in `TestResults/*.trx`.
