Skip to content

Commit 09afb0f

Browse files
committed
test: add validation tests for signature lexicons
- validate-signature-inline: inline signature with all JOSE algorithms, required fields, open knownValues - validate-signature-proof: proof record with CID, optional fields, requirement, datetime validation - validate-signatures-on-record: signatures array on activity records with inline, remote (strongRef), mixed, and empty signatures
1 parent 9355f01 commit 09afb0f

3 files changed

Lines changed: 233 additions & 0 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { describe, it, expect } from "vitest";
2+
import { validate, ids } from "../generated/lexicons.js";
3+
import * as SignatureInline from "../generated/types/app/certified/signature/inline.js";
4+
5+
describe("app.certified.signature.inline", () => {
6+
it("should accept a valid inline signature with all fields", () => {
7+
const result = SignatureInline.validateMain({
8+
$type: "app.certified.signature.inline",
9+
signatureType: "ES256K",
10+
signature: new Uint8Array([1, 2, 3, 4]),
11+
key: "did:plc:abc123#signing",
12+
});
13+
expect(result.success).toBe(true);
14+
if (result.success) {
15+
expect(result.value.key).toBe("did:plc:abc123#signing");
16+
expect(result.value.signatureType).toBe("ES256K");
17+
}
18+
});
19+
20+
it("should accept a signature without optional signatureType", () => {
21+
const result = SignatureInline.validateMain({
22+
$type: "app.certified.signature.inline",
23+
signature: new Uint8Array([1, 2, 3, 4]),
24+
key: "did:plc:abc123#signing",
25+
});
26+
expect(result.success).toBe(true);
27+
});
28+
29+
it("should accept all known algorithm identifiers", () => {
30+
for (const alg of ["ES256", "ES256K", "Ed25519"]) {
31+
const result = SignatureInline.validateMain({
32+
$type: "app.certified.signature.inline",
33+
signatureType: alg,
34+
signature: new Uint8Array([0xde, 0xad]),
35+
key: "did:plc:test#key-1",
36+
});
37+
expect(result.success).toBe(true);
38+
}
39+
});
40+
41+
it("should accept unknown algorithm identifiers (knownValues is open)", () => {
42+
const result = SignatureInline.validateMain({
43+
$type: "app.certified.signature.inline",
44+
signatureType: "PS256",
45+
signature: new Uint8Array([0xca, 0xfe]),
46+
key: "did:web:example.com#key-1",
47+
});
48+
expect(result.success).toBe(true);
49+
});
50+
51+
it("should reject when required field 'signature' is missing", () => {
52+
const result = validate(
53+
{
54+
key: "did:plc:abc123#signing",
55+
},
56+
ids.AppCertifiedSignatureInline,
57+
"main",
58+
false,
59+
);
60+
expect(result.success).toBe(false);
61+
});
62+
63+
it("should reject when required field 'key' is missing", () => {
64+
const result = validate(
65+
{
66+
signature: new Uint8Array([1, 2, 3]),
67+
},
68+
ids.AppCertifiedSignatureInline,
69+
"main",
70+
false,
71+
);
72+
expect(result.success).toBe(false);
73+
});
74+
});
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { describe, it, expect } from "vitest";
2+
import { validate, ids } from "../generated/lexicons.js";
3+
import * as SignatureProof from "../generated/types/app/certified/signature/proof.js";
4+
5+
describe("app.certified.signature.proof", () => {
6+
it("should accept a valid proof with all fields", () => {
7+
const result = SignatureProof.validateMain({
8+
$type: "app.certified.signature.proof",
9+
cid: "bafyreie5737gdxlw5i64vngml6xvqeatqy3a4erphoqtso54z2eooh4zae",
10+
note: "Verified by platform quality assurance process",
11+
createdAt: "2024-06-15T12:00:00.000Z",
12+
});
13+
expect(result.success).toBe(true);
14+
if (result.success) {
15+
expect(result.value.cid).toBe(
16+
"bafyreie5737gdxlw5i64vngml6xvqeatqy3a4erphoqtso54z2eooh4zae",
17+
);
18+
expect(result.value.note).toBe(
19+
"Verified by platform quality assurance process",
20+
);
21+
}
22+
});
23+
24+
it("should accept a proof with only the required cid field", () => {
25+
const result = SignatureProof.validateMain({
26+
$type: "app.certified.signature.proof",
27+
cid: "bafyreie5737gdxlw5i64vngml6xvqeatqy3a4erphoqtso54z2eooh4zae",
28+
});
29+
expect(result.success).toBe(true);
30+
});
31+
32+
it("should reject when required field 'cid' is missing", () => {
33+
const result = validate(
34+
{
35+
note: "Some note",
36+
createdAt: "2024-06-15T12:00:00.000Z",
37+
},
38+
ids.AppCertifiedSignatureProof,
39+
"main",
40+
false,
41+
);
42+
expect(result.success).toBe(false);
43+
});
44+
45+
it("should require $type since proof is a record type", () => {
46+
const result = validate(
47+
{
48+
cid: "bafyreie5737gdxlw5i64vngml6xvqeatqy3a4erphoqtso54z2eooh4zae",
49+
},
50+
ids.AppCertifiedSignatureProof,
51+
"main",
52+
true,
53+
);
54+
expect(result.success).toBe(false);
55+
});
56+
57+
it("should reject an invalid datetime format", () => {
58+
const result = validate(
59+
{
60+
cid: "bafyreie5737gdxlw5i64vngml6xvqeatqy3a4erphoqtso54z2eooh4zae",
61+
createdAt: "not-a-date",
62+
},
63+
ids.AppCertifiedSignatureProof,
64+
"main",
65+
false,
66+
);
67+
expect(result.success).toBe(false);
68+
});
69+
});
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { describe, it, expect } from "vitest";
2+
import { ids } from "../generated/lexicons.js";
3+
import * as Activity from "../generated/types/org/hypercerts/claim/activity.js";
4+
5+
/**
6+
* Tests that the `signatures` property works correctly on record lexicons.
7+
* Uses activity as a representative — all 20 record lexicons share the
8+
* same signatures property definition.
9+
*/
10+
describe("signatures property on records", () => {
11+
const baseActivity = {
12+
title: "Test Activity",
13+
shortDescription: "A test activity for signature validation",
14+
createdAt: "2024-06-15T12:00:00.000Z",
15+
};
16+
17+
it("should accept a record without signatures", () => {
18+
const result = Activity.validateMain({
19+
$type: ids.OrgHypercertsClaimActivity,
20+
...baseActivity,
21+
});
22+
expect(result.success).toBe(true);
23+
});
24+
25+
it("should accept a record with an inline signature", () => {
26+
const result = Activity.validateMain({
27+
$type: ids.OrgHypercertsClaimActivity,
28+
...baseActivity,
29+
signatures: [
30+
{
31+
$type: "app.certified.signature.inline",
32+
signatureType: "ES256K",
33+
signature: new Uint8Array([1, 2, 3, 4]),
34+
key: "did:plc:platform123#signing",
35+
},
36+
],
37+
});
38+
expect(result.success).toBe(true);
39+
});
40+
41+
it("should accept a record with a remote attestation (strongRef)", () => {
42+
const result = Activity.validateMain({
43+
$type: ids.OrgHypercertsClaimActivity,
44+
...baseActivity,
45+
signatures: [
46+
{
47+
$type: "com.atproto.repo.strongRef",
48+
uri: "at://did:plc:verifier/app.certified.signature.proof/3k2abc",
49+
cid: "bafyreie5737gdxlw5i64vngml6xvqeatqy3a4erphoqtso54z2eooh4zae",
50+
},
51+
],
52+
});
53+
expect(result.success).toBe(true);
54+
});
55+
56+
it("should accept a record with both inline and remote signatures", () => {
57+
const result = Activity.validateMain({
58+
$type: ids.OrgHypercertsClaimActivity,
59+
...baseActivity,
60+
signatures: [
61+
{
62+
$type: "app.certified.signature.inline",
63+
signature: new Uint8Array([0xde, 0xad]),
64+
key: "did:plc:signer1#key-1",
65+
},
66+
{
67+
$type: "com.atproto.repo.strongRef",
68+
uri: "at://did:plc:verifier/app.certified.signature.proof/abc123",
69+
cid: "bafyreie5737gdxlw5i64vngml6xvqeatqy3a4erphoqtso54z2eooh4zae",
70+
},
71+
{
72+
$type: "app.certified.signature.inline",
73+
signatureType: "Ed25519",
74+
signature: new Uint8Array([0xca, 0xfe]),
75+
key: "did:plc:signer2#key-2",
76+
},
77+
],
78+
});
79+
expect(result.success).toBe(true);
80+
});
81+
82+
it("should accept an empty signatures array", () => {
83+
const result = Activity.validateMain({
84+
$type: ids.OrgHypercertsClaimActivity,
85+
...baseActivity,
86+
signatures: [],
87+
});
88+
expect(result.success).toBe(true);
89+
});
90+
});

0 commit comments

Comments
 (0)