nip/tests/test_package_metadata.nim

206 lines
7.1 KiB
Nim

## Property-Based Tests for Package Metadata (metadata.json)
##
## **Feature:** 01-nip-unified-storage-and-formats
## **Property 10:** Provenance Preservation
## **Validates:** Requirements 7.1, 7.2, 7.3
##
## **Property Statement:**
## For any package, the metadata.json SHALL accurately reflect the complete build chain
##
## **TDD Strategy:**
## Test-driven approach to ensure complete provenance tracking across all formats.
import std/[unittest, times, options, tables, json]
import nip/package_metadata
import nip/manifest_parser
# ============================================================================
# Helper: Create Maximally Populated Metadata
# ============================================================================
proc createFullMetadata*(formatType: string): PackageMetadata =
## Create maximally populated metadata for testing
## Tests all fields including optional ones
var envVars = initTable[string, string]()
envVars["CC"] = "gcc"
envVars["CFLAGS"] = "-O2"
envVars["PATH"] = "/usr/bin:/bin"
var extensibleMeta = initTable[string, string]()
extensibleMeta["custom_field"] = "custom_value"
extensibleMeta["build_id"] = "12345"
result = PackageMetadata(
formatType: formatType,
formatVersion: "1.0",
name: "test-package",
version: "1.2.3-alpha.1+build.2",
description: "Test package for metadata validation",
license: "MIT",
tags: @["test", "metadata", "provenance"],
source: SourceProvenance(
origin: "https://github.com/test/package",
sourceHash: "xxh3-source-abc123",
upstream: some("https://upstream.org/package"),
upstreamVersion: some("1.2.3"),
fetchedAt: parse("2025-11-20T10:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"),
fetchMethod: "git"
),
build: BuildProvenance(
buildTimestamp: parse("2025-11-20T11:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"),
builder: "ci-bot@nexus.os",
buildHost: "build-server-01",
buildDuration: some(3600),
buildEnvironment: BuildEnvironment(
compilerVersion: "gcc-13.2.0",
compilerFlags: @["-O3", "-march=native", "-flto"],
configureFlags: @["--prefix=/usr", "--enable-shared"],
targetArchitecture: "x86_64",
libc: "musl",
allocator: "jemalloc",
buildSystem: "cmake",
environmentVars: envVars
)
),
installation: InstallationProvenance(
installedAt: parse("2025-11-20T12:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"),
installedBy: "admin",
installPath: "/Programs/TestPackage/1.2.3",
installMethod: "nip install",
installHost: "workstation-01"
),
hashes: IntegrityHashes(
sourceHash: "xxh3-source-abc123",
buildHash: "xxh3-build-def456",
artifactHash: "xxh3-artifact-ghi789",
manifestHash: "xxh3-manifest-jkl012"
),
signatures: @[
SignatureRecord(
algorithm: "ed25519",
keyId: "builder-key-2024",
signature: "base64-signature-1",
signedBy: "builder@nexus.os",
signedAt: parse("2025-11-20T11:30:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'")
),
SignatureRecord(
algorithm: "ed25519",
keyId: "maintainer-key-2024",
signature: "base64-signature-2",
signedBy: "maintainer@nexus.os",
signedAt: parse("2025-11-20T11:45:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'")
)
],
metadata: extensibleMeta
)
# ============================================================================
# Property-Based Tests
# ============================================================================
suite "Package Metadata Property Tests (Task 9.1)":
test "Property 10: JSON Generation - Verify structure":
## **Phase 1:** Test JSON generation in isolation
let metadata = createFullMetadata("npk")
let jsonString = generateMetadataJson(metadata)
echo "\n=== Generated JSON (Phase 1) ===\n"
echo jsonString
echo "\n=== End JSON ===\n"
# Verify JSON structure
let json = parseJson(jsonString)
check json.hasKey("format_type")
check json.hasKey("source_provenance")
check json.hasKey("build_provenance")
check json.hasKey("installation_provenance")
check json.hasKey("integrity_hashes")
check json.hasKey("signatures")
# Verify required fields
check json["format_type"].getStr() == "npk"
check json["name"].getStr() == "test-package"
check json["source_provenance"]["source_hash"].getStr().startsWith("xxh3-")
check json["integrity_hashes"]["build_hash"].getStr().startsWith("xxh3-")
test "Property 10: Full Roundtrip - ALL FIELDS":
## **Phase 2:** Full roundtrip test
##
## **Feature: 01-nip-unified-storage-and-formats, Property 10: Provenance Preservation**
## **Validates: Requirements 7.1, 7.2, 7.3**
for formatType in ["npk", "nip", "nexter"]:
let originalMetadata = createFullMetadata(formatType)
# Step 1: Generate JSON
let jsonString = generateMetadataJson(originalMetadata)
check jsonString.len > 0
# Step 2: Parse JSON back
let parsedMetadata = parseMetadataJson(jsonString)
# Step 3: Verify ALL fields preserved
check parsedMetadata.formatType == originalMetadata.formatType
check parsedMetadata.name == originalMetadata.name
check parsedMetadata.version == originalMetadata.version
check parsedMetadata.hashes.buildHash == originalMetadata.hashes.buildHash
check parsedMetadata.hashes.sourceHash == originalMetadata.hashes.sourceHash
check parsedMetadata.build.builder == originalMetadata.build.builder
check parsedMetadata.source.origin == originalMetadata.source.origin
# Step 4: Verify deterministic generation
let jsonString2 = generateMetadataJson(parsedMetadata)
# Parse both to compare structure (JSON key order may vary)
let json1 = parseJson(jsonString)
let json2 = parseJson(jsonString2)
check json1 == json2
suite "Package Metadata Validation Tests":
test "Validate metadata with valid xxh3 hashes":
let validMetadata = createFullMetadata("npk")
let issues = validateMetadata(validMetadata)
check issues.len == 0
test "Reject metadata with invalid hash format":
var invalidMetadata = createFullMetadata("npk")
invalidMetadata.hashes.buildHash = "sha256-invalid"
let issues = validateMetadata(invalidMetadata)
check issues.len > 0
check issues[0].contains("xxh3-128")
test "Reject metadata with invalid signature algorithm":
var invalidMetadata = createFullMetadata("npk")
invalidMetadata.signatures[0].algorithm = "rsa"
let issues = validateMetadata(invalidMetadata)
check issues.len > 0
check issues[0].contains("ed25519")
test "Property 10: Determinism - Same input produces same JSON structure":
## Verify that generateMetadataJson is deterministic
## (JSON key order may vary, but structure should be identical)
let metadata = createFullMetadata("nip")
# Generate JSON multiple times
let json1 = parseJson(generateMetadataJson(metadata))
let json2 = parseJson(generateMetadataJson(metadata))
let json3 = parseJson(generateMetadataJson(metadata))
# All outputs should be structurally identical
check json1 == json2
check json2 == json3
check json1 == json3