P2.1: address review — harden artifact validation, release/artifact fields
- validate_artifact: add empty content_type -> MissingField; size_bytes <= 0 -> new BadSize; sha256 not exactly 64 lowercase-hex -> new BadDigest (via validate_sha256). The valid fixture (64-hex sha, positive size, non-empty content_type) stays accepted. - Release: rename updated_at -> published_at (published_at = 0 means draft). Final order: id, app_id, version, build, channel, notes, created_by, created_at, published_at. - Artifact: add opaque metadata: string before validation_status. - tests: add empty-content_type/MissingField, bad-size/BadSize, malformed-sha256/BadDigest cases and contract pins reading back Release.published_at and Artifact.metadata. PO-ruled out of scope and NOT added: Token model, in-memory repository (P2.3).
This commit is contained in:
@@ -37,7 +37,7 @@ valid_release :: () -> Release {
|
||||
notes = "first cut",
|
||||
created_by = "user_01",
|
||||
created_at = 1700000000,
|
||||
updated_at = 1700000000,
|
||||
published_at = 1700000200,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ valid_artifact :: () -> Artifact {
|
||||
size_bytes = 10485760,
|
||||
sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
storage_key = "apps/app_01/rel_01/acme.apk",
|
||||
metadata = "{\"min_os\":\"14\"}",
|
||||
validation_status = .valid,
|
||||
};
|
||||
}
|
||||
@@ -140,14 +141,43 @@ check_rejects_bad_channel :: () -> bool {
|
||||
return matched;
|
||||
}
|
||||
|
||||
check_rejects_missing_field :: () -> bool {
|
||||
check_rejects_empty_content_type :: () -> bool {
|
||||
a := valid_artifact();
|
||||
a.sha256 = ""; // required field cleared
|
||||
a.content_type = ""; // required string cleared
|
||||
matched := false;
|
||||
validate_artifact(a) catch e { matched = (e == error.MissingField); };
|
||||
return matched;
|
||||
}
|
||||
|
||||
check_rejects_bad_size :: () -> bool {
|
||||
a := valid_artifact();
|
||||
a.size_bytes = -1; // a content-addressed artifact has positive bytes
|
||||
matched := false;
|
||||
validate_artifact(a) catch e { matched = (e == error.BadSize); };
|
||||
return matched;
|
||||
}
|
||||
|
||||
check_rejects_bad_digest :: () -> bool {
|
||||
a := valid_artifact();
|
||||
a.sha256 = "not-a-sha"; // not 64 lowercase-hex chars
|
||||
matched := false;
|
||||
validate_artifact(a) catch e { matched = (e == error.BadDigest); };
|
||||
return matched;
|
||||
}
|
||||
|
||||
// Contract pins: read back the renamed Release.published_at and the new
|
||||
// Artifact.metadata. These references fail to compile against the pre-change
|
||||
// structs (Release.updated_at / no Artifact.metadata).
|
||||
check_release_published_at :: () -> bool {
|
||||
r := valid_release();
|
||||
return r.published_at == 1700000200;
|
||||
}
|
||||
|
||||
check_artifact_metadata :: () -> bool {
|
||||
a := valid_artifact();
|
||||
return a.metadata == "{\"min_os\":\"14\"}";
|
||||
}
|
||||
|
||||
run_case :: (label: string, ok: bool) -> s32 {
|
||||
if ok { print(" PASS {}\n", label); return 0; }
|
||||
print(" FAIL {}\n", label);
|
||||
@@ -166,7 +196,11 @@ main :: () -> s32 {
|
||||
failures += run_case("reject: bad version -> BadVersion", check_rejects_bad_version());
|
||||
failures += run_case("reject: unknown platform -> UnknownPlatform", check_rejects_unknown_platform());
|
||||
failures += run_case("reject: bad channel -> BadChannelName", check_rejects_bad_channel());
|
||||
failures += run_case("reject: missing field -> MissingField", check_rejects_missing_field());
|
||||
failures += run_case("reject: empty content_type -> MissingField", check_rejects_empty_content_type());
|
||||
failures += run_case("reject: non-positive size_bytes -> BadSize", check_rejects_bad_size());
|
||||
failures += run_case("reject: malformed sha256 -> BadDigest", check_rejects_bad_digest());
|
||||
failures += run_case("contract: Release.published_at readback", check_release_published_at());
|
||||
failures += run_case("contract: Artifact.metadata readback", check_artifact_metadata());
|
||||
|
||||
print("------------------------------------------------\n");
|
||||
if failures == 0 {
|
||||
|
||||
Reference in New Issue
Block a user