Skip to content

[quality] data/awards.json <-> AwardsTimeline render contract is untested; validate-awards.mjs is blind to 8 mutations that misrender /awards #359

Closed

Description

@hivecommons-hive

Finding

scripts/validate-awards.mjs is the only gate on data/awards.json, and it is
deliberately tolerant: it checks truthiness, an ^https:// prefix, and a year
floor. src/components/AwardsTimeline/index.js is stricter than that in several
places, so an awards file that passes npm run validate:awards can still render
a wrong or misleading /awards page.

No test asserts the data against the component. tests/validate-awards.test.mjs
exercises the validator against synthetic fixtures only; it never reads the
real data/awards.json, and nothing in tests/ reads
src/components/AwardsTimeline/index.js.

Evidence

Verified on a clean clone at 00b44df, node v26.8.2, 2026-09-20.

Nine single-field mutations were applied to data/awards.json, one at a time.
Eight of the nine pass npm run validate:awards:

mutation validate:awards what the page does
verifiedAgainst repointed to another host PASS misattributes its own audit source (see below)
verifiedAt set to a future date PASS banner claims an audit that has not happened
citation set to a number PASS renders 5 as the citation
event set to " " PASS renders a blank line
slug set to Acme_Corp PASS non-kebab React key
duplicate winner entry inserted PASS winner rendered twice, no warning
announcementUrl set to https:// PASS new URL() throws; non-navigable anchor
two awardLabels for one award slug PASS reads as two different awards
out-of-range year (1999) FAIL already caught

The verifiedAgainst case is the sharpest. AwardsTimeline hard-codes the
visible link text
:

<a href={awardsData.verifiedAgainst} target="_blank" rel="noreferrer">
  contribute.cncf.io/community/awards
</a>

The validator accepts any https:// URL for verifiedAgainst, so the href and
the label it is rendered under can drift apart and the page then misattributes
the provenance of its own winner history — exactly the claim the banner exists
to make.

Why existing gates miss it

Coverage evidence

Recommendation

One test-only PR closes this. No production change is required — every
assertion below is green against main as it stands today.

  • Add tests/awards-data.test.mjs asserting the real data/awards.json
    against the contract AwardsTimeline depends on: top-level keys present;
    verifiedAt a non-future YYYY-MM-DD; verifiedAgainst host+path equal
    to the label the component hard-codes; every rendered text field a
    non-empty string; kebab-case slug and integer year; newest-first
    ordering; no duplicate year/slug; every present link a resolvable
    https URL with a host; every card has a primary link; one display label
    per award slug; and a reverse guard that fails if WinnerCard starts
    destructuring a field the contract does not cover.

Priority

  • Impact: medium — the /awards page is the site's public record of CNCF End
    User award winners, and the failure mode is silent misattribution rather than
    a broken build.
  • Effort: low — one new test file, no dependencies, no production change.

— hive: agent=quality backend=copilot model=claude-opus-5

Activity

  1. added
    qualityApproved by a Hive merger/owner for auto-merge on green CI
    testingApproved by a Hive merger/owner for auto-merge on green CI
    agent/qualityApproved by a Hive merger/owner for auto-merge on green CI
    on Sep 20, 2026
  2. added a commit that references this issue on Sep 21, 2026
    2d553dd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/qualityApproved by a Hive merger/owner for auto-merge on green CIqualityApproved by a Hive merger/owner for auto-merge on green CItestingApproved by a Hive merger/owner for auto-merge on green CI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions