Skip to content

test: contract tests improving coverage (toward #79)#81

Merged
markovejnovic merged 1 commit into
harmont-dev:mainfrom
jrimmer:test/coverage-contracts-79
Jul 12, 2026
Merged

test: contract tests improving coverage (toward #79)#81
markovejnovic merged 1 commit into
harmont-dev:mainfrom
jrimmer:test/coverage-contracts-79

Conversation

@jrimmer

@jrimmer jrimmer commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Toward #79 (>=95% coverage).

What

Each addition targets a Contract path — a wire-format conversion, a gRPC status classification, or a refusal — not a getter or a pass-through. Triage per AGENTS.md (Contract / Mechanical / Privileged):

Module Before After Contract added
hyper/grpc/codec.ex 54% 100% from_grpc/to_grpc wire contracts + the rpc_error status-classification table (every domain reason → its promised gRPC status)
hyper/cfg/network.ex 86% 100% non-string network.uplinkArgumentError
hyper/cfg/grpc.ex 89% 100% plaintext-default cred(nil) via load/0
hyper/cluster/routing.ex 85% 100% register_self already-registered + all/0 node mapping (new test file; reuses the named singleton to avoid a start_supervised race)
hyper/cfg/budget.ex 80% 90% non-number / bad unit string / wrong type → specific error tuples (table-driven)
redist/file.ex 87% 93% {:install_failed, _} on an unwritable dest
redist/targz.ex 88% 94% {:extract_failed, _} on a corrupt archive with a valid checksum

+20 tests, table-driven where the assertion shape repeats. mix format --check-formatted and mix credo --strict are clean on all of them.

Deliberately declined

Per AGENTS.md's "Answering coverage reports" — these gaps are not worth a unit test, with rationale:

  • Mechanicalredist download_error Req pass-throughs (already declined in those modules' own moduledocs); cfg/budget.get/0 getter; cfg/budget's required/3 :error branches (a default is always present, so they are unreachable).
  • Privileged / integration-onlycfg/toml.ex (needs root to write /etc/hyper/config.toml; the absent-file -> %{} default is covered), sys/linux/cgroup/v2.ex (cgroupfs), sys/linux/subid.ex (/etc/subuid+/etc/subgid; the ranges/1 parsing is fixture-tested), and node/fire_vmm/{meter,daemon,relay,guest_agent} (need live cgroup / Firecracker / sockets / the built musl agent — covered in CI's integration job).

A patch may legitimately merge below 100%; these uncovered mechanical lines cost less than tests that pin implementation detail.

Notes

  • The codec status-classification table is the security/correctness-relevant contract: it pins which gRPC status each domain error maps to, so a mutation swapping two statuses would fail a test rather than silently change what clients see.
  • RoutingTest starts the registry with start_supervised (non-bang) and reuses an already-started one, since Hyper.Cluster.Routing is a named singleton and HyperTest also starts it — a bang call raced under async.

GLM-5.2 did this work.

Closes toward #79.

…uting

Toward harmont-dev#79 (>=95% coverage). Each addition targets a real Contract path —
a wire-format conversion, a gRPC status classification, or a refusal —
not a getter or a pass-through. Triage per AGENTS.md:

  grpc/codec.ex      54% -> 100%  from_grpc/to_grpc wire contracts + the
                                rpc_error status-classification table (every
                                domain reason -> its promised gRPC status).
  cfg/network.ex     86% -> 100%  non-string network.uplink -> ArgumentError.
  cfg/grpc.ex        89% -> 100%  plaintext-default cred(nil) via load/0.
  cluster/routing    85% -> 100%  register_self already-registered + all/0 node
                                mapping (new test file; reuses the named
                                singleton to avoid a start_supervised race).
  cfg/budget.ex      80% -> 90%   non-number / bad unit string / wrong type
                                -> specific error tuples (table-driven).
  redist/file.ex     87% -> 93%   {:install_failed, _} on an unwritable dest.
  redist/targz.ex    88% -> 94%   {:extract_failed, _} on a corrupt archive
                                with a valid checksum.

Deliberately declined (mechanical / privileged), with rationale recorded:

  - redist download_error Req pass-throughs (already declined in moduledocs);
  - cfg/budget.get/0 getter and the dead required/3 :error branches
    (a default is always present, so they are unreachable);
  - cfg/toml.ex /sys/fs/cgroup /etc/subuid /etc/subgid (need Linux root/cgroupfs);
  - node/fire_vmm/{meter,daemon,relay,guest_agent} (integration-only: cgroup,
    Firecracker, sockets, built musl agent — covered in CI's integration job).

All +20 tests pass; mix format --check-formatted and mix credo --strict clean.
Comment on lines +53 to +57
test "with no cred configured, load returns nil (plaintext)" do
# The plaintext default: an absent cred must yield nil, never a defaulted
# credential that would silently enable TLS.
assert Grpc.load().cred == nil
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair

Comment on lines +83 to +101
# `from_grpc/1` -- the decode boundary. The contract: an inbound CreateVmRequest
# maps to a domain Spec with the right field placement, or is refused with a
# specific reason the server then classifies to a gRPC status.
describe "from_grpc/1 CreateVmRequest" do
test "a well-formed request decodes to a Spec preserving every field" do
assert {:ok,
%Spec{
img_id: "img",
type: :micro,
arch: :aarch64,
boot_args: "console=ttyS0"
}} =
Codec.from_grpc(%CreateVmRequest{
img_id: "img",
instance_type: :INSTANCE_TYPE_MICRO,
arch: :ARCHITECTURE_AARCH64,
boot_args: "console=ttyS0"
})
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally don't see that much utility in these tests frankly. I will be honest I mostly vibe-coded these tests, so that's how we ended up with codec_test.exs, but in the future I'd like to see these tests replaced with e2e grpc tests.

i'm comfortable with your PR, considering the existing precedent =)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, more following through on the issue's ask. I'l look into creating an e2e ask and see if I can help with that.

Comment on lines +94 to +97
test "install/3 surfaces a corrupt archive as {:extract_failed, _}", %{
server: server,
dest: dest
} do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines +17 to +32
# Under `mix test --no-start` the app tree (which provisions Routing) is
# absent; start a test-scoped registry so these contracts are testable
# without the full supervision tree. Routing is a named singleton, so if
# another test (e.g. HyperTest) already started it, reuse that one rather
# than racing on start_supervised!/1.
if Process.whereis(Routing) do
:ok
else
_ = Application.ensure_all_started(:horde)

case start_supervised(Routing) do
{:ok, _pid} -> :ok
# Lost the start race to a parallel test: reuse the existing registry.
{:error, {:already_started, _pid}} -> :ok
end
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my gut feeling is that this is going to cause some flaky tests in the future, but OK, good enough for now

@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

Test Results

    4 files  ± 0  227 suites  +2   2m 26s ⏱️ - 1m 58s
  669 tests +20  669 ✅ +20    0 💤 ± 0  0 ❌ ±0 
1 139 runs  +40  669 ✅ +20  470 💤 +20  0 ❌ ±0 

Results for commit a79c935. ± Comparison against base commit 155b7f9.

@markovejnovic markovejnovic merged commit f6f88dd into harmont-dev:main Jul 12, 2026
8 checks passed
@markovejnovic

Copy link
Copy Markdown
Contributor

thank you for your PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants