PrintToVeraeTimeStamp/internal/pdfcert/pdfcert_test.go
George Lambert 48da24a661 feat: 2026 brand UI — statuslang, tray/menubar, CERT pdf, site kit
- Brand receipt status strings (statuslang) for notify/queue
- Windows tray + Start Menu/Startup install; macOS menubar helper
- CERT-01 plum/gold PDF + multihash/blake3 when present
- printit.verae.ai brand kit, queue page, customer-auth helper
- verae-print pending/tray-status/menubar commands
2026-10-13 21:11:32 -04:00

235 lines
7.8 KiB
Go

package pdfcert
import (
"bytes"
"strings"
"testing"
)
func TestBuildBrandedRegistration(t *testing.T) {
b, err := Build(Input{
DocumentName: "Contract.pdf",
SHA256: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
ReceiptID: "QmTestReceipt123",
CreatedAt: "2026-10-03T12:00:00Z",
Merkle: "113198",
Scope: "global",
ByteLength: 4096,
})
if err != nil {
t.Fatal(err)
}
if !bytes.HasPrefix(b, []byte("%PDF")) {
t.Fatal("not a pdf")
}
if len(b) < 5000 {
t.Fatalf("pdf too small (%d) — logo/qr missing?", len(b))
}
s := string(b)
if strings.Contains(s, "mcp.verae.ai") {
t.Fatal("must not mention mcp.verae.ai")
}
if strings.Contains(s, "via MCP") {
t.Fatal("must not say via MCP")
}
// CERT-01 title (full phrase or split across PDF text operators)
if !strings.Contains(s, "Verae Timestamp Certificate") && !strings.Contains(s, "Timestamp Certificate") {
t.Fatal("missing CERT-01 title")
}
if !strings.Contains(s, "only the digest was submitted") && !strings.Contains(s, "SHA-256 digest was submitted") {
t.Fatal("missing digests-only disclaimer")
}
hasFinal := strings.Contains(s, "final receipt") || strings.Contains(s, "Final:") || strings.Contains(s, "finalized") || strings.Contains(s, "FINAL")
if !hasFinal {
t.Fatal("missing final-only evidence language")
}
// Multihash when SHA-256 is on the receipt/input (CERT-01).
if !strings.Contains(s, "f1220") {
t.Fatal("expected content multihash f1220… when SHA-256 available")
}
if !strings.Contains(s, "not proof") && !strings.Contains(s, "jobId") {
t.Fatal("expected jobId/hold not-proof disclaimer")
}
if !strings.Contains(s, "explorer.verae.ai") {
t.Fatal("expected explorer lookup")
}
if !strings.Contains(s, "Substrate") {
t.Fatal("expected Verae Substrate chain wording")
}
// Clickable link annotations (jsPDF textWithLink / link, or gofpdf LinkString)
if !strings.Contains(s, "/URI") && !strings.Contains(s, "/URI(") {
if !strings.Contains(s, "URI") || !strings.Contains(s, "explorer.verae.ai/time") {
t.Fatal("expected clickable URI annotation for public lookup URL")
}
}
if !strings.Contains(s, "/time?") || !strings.Contains(s, "QmTestReceipt123") {
t.Fatal("expected /time? deep link with receipt in PDF")
}
}
func TestFinalBannerTitle(t *testing.T) {
if got := finalBannerTitle("113198"); got != "Final: Substrate block #113,198, finalized" {
t.Fatalf("got %q", got)
}
if got := finalBannerTitle(""); got != "Final: Substrate block #N, finalized" {
t.Fatalf("got %q", got)
}
if got := finalBannerTitle("0xabc"); got != "Final: Substrate block #N, finalized" {
t.Fatalf("got %q", got)
}
}
func TestContentMultihash(t *testing.T) {
hex := "20515740e1051f69c552b83ff1a919398feca768090930a4a502f9335273f279"
want := "f1220" + hex
if got := ContentMultihash(hex); got != want {
t.Fatalf("got %q want %q", got, want)
}
if got := ContentMultihash("0x" + hex); got != want {
t.Fatalf("0x prefix: %q", got)
}
if ContentMultihash("") != "" || ContentMultihash("not-a-hash") != "" {
t.Fatal("empty/invalid must omit multihash")
}
}
func TestContentBlake3Multihash(t *testing.T) {
hex := "aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899"
want := "f1e20" + hex
if got := ContentBlake3Multihash(hex); got != want {
t.Fatalf("got %q want %q", got, want)
}
if got := ContentBlake3Multihash("0x" + hex); got != want {
t.Fatalf("0x prefix: %q", got)
}
if ContentBlake3Multihash("") != "" || ContentBlake3Multihash("short") != "" {
t.Fatal("empty/invalid must omit blake3 multihash")
}
}
func TestResolveDigestsOmitWhenEmpty(t *testing.T) {
mh, b3, b3mh := ResolveDigests(Input{})
if mh != "" || b3 != "" || b3mh != "" {
t.Fatalf("empty input must omit all digests, got mh=%q b3=%q b3mh=%q", mh, b3, b3mh)
}
// Invalid lengths must not invent multihash prefixes.
mh, b3, b3mh = ResolveDigests(Input{SHA256: "abc", Blake3: "not-64-hex"})
if mh != "" || b3mh != "" {
t.Fatalf("invalid digests must not yield multihash, got mh=%q b3mh=%q", mh, b3mh)
}
if b3 != "not-64-hex" {
t.Fatalf("raw blake3 hex is passed through when set: %q", b3)
}
}
func TestResolveDigestsFromUpstream(t *testing.T) {
sha := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
b3 := "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
mh, gotB3, b3mh := ResolveDigests(Input{SHA256: sha, Blake3: b3})
if mh != "f1220"+sha {
t.Fatalf("mh: %q", mh)
}
if gotB3 != b3 {
t.Fatalf("blake3 hex: %q", gotB3)
}
if b3mh != "f1e20"+b3 {
t.Fatalf("blake3 mh: %q", b3mh)
}
// Explicit multihash wins over derivation.
mh, _, b3mh = ResolveDigests(Input{
SHA256: sha,
Multihash: "f1220deadbeef",
Blake3: b3,
Blake3Multihash: "f1e20cafebabe",
})
if mh != "f1220deadbeef" || b3mh != "f1e20cafebabe" {
t.Fatalf("explicit: mh=%q b3mh=%q", mh, b3mh)
}
}
func TestBuildIncludesMultihashWhenSHAAvailable(t *testing.T) {
hex := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
b, err := Build(Input{
DocumentName: "Board.pdf",
SHA256: hex,
ReceiptID: "QmTestReceipt123",
CreatedAt: "2026-10-03T12:00:00Z",
Merkle: "1284530",
})
if err != nil {
t.Fatal(err)
}
s := string(b)
if !strings.Contains(s, "f1220"+hex) && !strings.Contains(s, "Content multihash") {
// PDF may split text operators; at least f1220 prefix + start of digest should appear.
if !strings.Contains(s, "f1220") || !strings.Contains(s, "0123456789abcdef") {
t.Fatal("expected content multihash when SHA-256 present")
}
}
if !strings.Contains(s, "Final: Substrate block") && !strings.Contains(s, "1,284,530") {
t.Fatal("expected statuslang final banner")
}
}
func TestBuildOmitsMultihashWithoutDigest(t *testing.T) {
b, err := Build(Input{
DocumentName: "Board.pdf",
ReceiptID: "QmTestReceipt123",
Merkle: "99",
})
if err != nil {
t.Fatal(err)
}
s := string(b)
if strings.Contains(s, "Content multihash") || strings.Contains(s, "f1220") {
t.Fatal("must not invent multihash without digest data")
}
if strings.Contains(s, "BLAKE3") || strings.Contains(s, "f1e20") || strings.Contains(s, "blake3") {
t.Fatal("must not invent blake3 without upstream digest")
}
}
func TestBuildFallbackIncludesBlake3WhenProvided(t *testing.T) {
// Exercise gofpdf path directly so node template (B3 lock) is not required.
sha := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
b3 := "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
in := Input{
DocumentName: "Board.pdf",
SHA256: sha,
Blake3: b3,
ReceiptID: "QmTestReceipt123",
CreatedAt: "2026-10-03T12:00:00Z",
Merkle: "99",
}
pdf, err := buildFallbackGofpdf(in, "Board.pdf", in.CreatedAt, "GLOBAL", in.Merkle, LookupURL(sha, in.ReceiptID))
if err != nil {
t.Fatal(err)
}
s := string(pdf)
if !strings.Contains(s, "f1e20"+b3) && !strings.Contains(s, "f1e20") {
t.Fatal("expected blake3 multihash when Blake3 provided")
}
if !strings.Contains(s, b3) && !strings.Contains(s, "BLAKE3") {
t.Fatal("expected blake3 hex line when Blake3 provided")
}
if !strings.Contains(s, "f1220") {
t.Fatal("expected sha2-256 multihash alongside blake3")
}
}
func TestLookupURLNoMCP(t *testing.T) {
u := LookupURL("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "QmTestReceipt123")
if strings.Contains(u, "mcp.verae.ai") {
t.Fatal(u)
}
if !strings.HasPrefix(u, "https://explorer.verae.ai/time?") {
t.Fatalf("want /time? deep link, got %s", u)
}
if !strings.Contains(u, "receipt=QmTestReceipt123") || !strings.Contains(u, "sha256=0123456789abcdef") {
t.Fatal(u)
}
// Must not use the old root query form that the scan bar does not honor
if strings.Contains(u, "explorer.verae.ai/?sha256=") {
t.Fatalf("old root URL form: %s", u)
}
}