lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit ab2a8feecba9740a6e9fe673f3b27e04f07ec952
parent 281f9b86804f23db66f1170c04bc06ce68a57a62
Author: triesap <tyson@radroots.org>
Date:   Tue,  4 Aug 2026 11:59:10 +0000

fix(package): close public development graph

- remove private test fixture dependencies from public crates
- retain package-owned identity and knowledge fixtures
- enforce private-edge rejection for every dependency kind
- align staged publication boundary assertions

Diffstat:
MCargo.lock | 3---
Mcrates/blossom/tests/package_boundary.rs | 2+-
Mcrates/event/tests/package_boundary.rs | 4++--
Mcrates/event_codec/Cargo.toml | 1-
Mcrates/event_codec/src/deletion/reconciliation_v1/admission/tests.rs | 2+-
Mcrates/event_codec/tests/knowledge_fixtures.rs | 6++++--
Mcrates/event_codec/tests/knowledge_manifest.rs | 4+++-
Mcrates/event_codec/tests/nip09_suppression_conformance.rs | 6++++--
Mcrates/event_codec/tests/package_boundary.rs | 4++--
Acrates/event_codec/tests/support/knowledge.rs | 521+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/event_codec/tests/support/mod.rs | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/event_codec/tests/verified_deletion_conformance.rs | 6++++--
Mcrates/geonames/tests/package_boundary.rs | 2+-
Mcrates/nostr/Cargo.toml | 1-
Mcrates/nostr/tests/food_availability_profile.rs | 4+++-
Mcrates/nostr/tests/nip09_deletion_request_profile.rs | 4+++-
Mcrates/nostr/tests/nip10_reply_profile.rs | 4+++-
Mcrates/nostr/tests/nip17_conformance.rs | 10++++++----
Mcrates/nostr/tests/nip22_comment_profile.rs | 4+++-
Mcrates/nostr/tests/package_boundary.rs | 6+++---
Acrates/nostr/tests/support.rs | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/nostr_connect/src/lib.rs | 4++--
Mcrates/nostr_connect/tests/package_boundary.rs | 4++--
Mcrates/protocol/tests/package_boundary.rs | 2+-
Mcrates/secrets/tests/package_boundary.rs | 2+-
Mcrates/signing/tests/package_boundary.rs | 2+-
Mcrates/storage_sqlite/tests/package_boundary.rs | 2+-
Mcrates/sync/tests/package_boundary.rs | 2+-
Mcrates/trade/Cargo.toml | 1-
Mcrates/trade/src/lib.rs | 3+++
Mcrates/trade/src/operational_listing/draft.rs | 2+-
Mcrates/trade/src/operational_listing/mutation.rs | 2+-
Mcrates/trade/src/operational_listing/validation.rs | 8++++----
Mcrates/trade/src/reducer_impl.rs | 2+-
Acrates/trade/src/test_fixtures.rs | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/trade/src/validation_receipt.rs | 6+++---
Mcrates/trade/src/workflow.rs | 2+-
Mcrates/trade/tests/package_boundary.rs | 2+-
Mcrates/transport/tests/package_boundary.rs | 2+-
Mcrates/transport_nostr/tests/conformance.rs | 2+-
Mcrates/transport_nostr/tests/package_boundary.rs | 2+-
Mtools/xtask/src/architecture.rs | 7+++++++
42 files changed, 962 insertions(+), 55 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -4557,7 +4557,6 @@ dependencies = [ "radroots_event", "radroots_identity", "radroots_protocol", - "radroots_test_fixtures", "secp256k1", "serde", "serde_json", @@ -4685,7 +4684,6 @@ dependencies = [ "radroots_identity", "radroots_protocol", "radroots_signing", - "radroots_test_fixtures", "serde", "serde_json", "thiserror 2.0.18", @@ -5153,7 +5151,6 @@ dependencies = [ "radroots_event", "radroots_event_codec", "radroots_identity", - "radroots_test_fixtures", "serde", "serde_json", "sha2", diff --git a/crates/blossom/tests/package_boundary.rs b/crates/blossom/tests/package_boundary.rs @@ -19,7 +19,7 @@ const ROOT: &str = include_str!("../src/lib.rs"); fn manifest_has_final_identity_features_and_no_radroots_dependencies() { assert!(MANIFEST.contains("name = \"radroots_blossom\"")); assert!(MANIFEST.contains("version = \"0.1.0-alpha\"")); - assert!(MANIFEST.contains("publish = false")); + assert!(MANIFEST.contains("publish = [\"crates-io\"]")); assert!(MANIFEST.contains("[lib]\nname = \"radroots_blossom\"")); assert!(MANIFEST.contains("default = [\"std\", \"serde\"]")); assert_eq!( diff --git a/crates/event/tests/package_boundary.rs b/crates/event/tests/package_boundary.rs @@ -31,7 +31,7 @@ const CODEC_PROFILE: &str = include_str!("../../event_codec/src/profile/mod.rs") fn manifest_has_final_identity_and_required_radroots_dependencies() { assert!(MANIFEST.contains("name = \"radroots_event\"")); assert!(MANIFEST.contains("version = \"0.1.0-alpha\"")); - assert!(MANIFEST.contains("publish = false")); + assert!(MANIFEST.contains("publish = [\"crates-io\"]")); assert!(MANIFEST.contains("[lib]\nname = \"radroots_event\"")); assert!(MANIFEST.contains("default = [\"std\", \"serde\"]")); assert_eq!( @@ -205,7 +205,7 @@ fn public_native_items_do_not_retain_the_legacy_radroots_prefix() { #[test] fn lossy_legacy_projections_are_quarantined_until_codec_retirement() { - assert!(CODEC_MANIFEST.contains("publish = false")); + assert!(CODEC_MANIFEST.contains("publish = [\"crates-io\"]")); for (source, compatibility_type) in [ (CODEC_POST_DECODE, "pub struct LegacyPost"), (CODEC_PROFILE, "pub struct LegacyProfile"), diff --git a/crates/event_codec/Cargo.toml b/crates/event_codec/Cargo.toml @@ -65,7 +65,6 @@ radroots_blossom = { workspace = true, default-features = false, features = [ "serde", "std", ] } -radroots_test_fixtures = { workspace = true } serde_json = { workspace = true, features = ["std"] } [lints] diff --git a/crates/event_codec/src/deletion/reconciliation_v1/admission/tests.rs b/crates/event_codec/src/deletion/reconciliation_v1/admission/tests.rs @@ -2,13 +2,13 @@ use super::*; #[test] fn admitted_contract_resolves_registry_entry() { + use crate::test_fixtures::FIXTURE_ALICE_SECRET_KEY_HEX; use nostr::secp256k1::Message; use nostr::{Keys, SECP256K1}; use radroots_event::{ envelope::EventEnvelopeParts, envelope::kind::KIND_DELETION_REQUEST, wire::compute_canonical_nip01_event_id, }; - use radroots_test_fixtures::FIXTURE_ALICE_SECRET_KEY_HEX; let keys = Keys::parse(FIXTURE_ALICE_SECRET_KEY_HEX).expect("fixed fixture secret key must parse"); diff --git a/crates/event_codec/tests/knowledge_fixtures.rs b/crates/event_codec/tests/knowledge_fixtures.rs @@ -1,5 +1,7 @@ #![cfg(feature = "knowledge")] +mod support; + use std::collections::BTreeSet; use radroots_event::contract::{ @@ -29,8 +31,8 @@ use radroots_event_codec::verification::{ RadrootsDecodeError, RadrootsDecodedEvent, RadrootsNip01VerificationError, verify_and_decode_radroots_event, }; -use radroots_test_fixtures::RELAY_PRIMARY_WSS; -use radroots_test_fixtures::knowledge::{ +use support::RELAY_PRIMARY_WSS; +use support::knowledge::{ RADROOTS_KNOWLEDGE_ADVERSARIAL_FIXTURES, RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS, RadrootsKnowledgeFixture, RadrootsKnowledgeFixtureCase, hex_64, knowledge_claim, knowledge_field_report, knowledge_valid_fixtures, wiki_article, wiki_merge_request, diff --git a/crates/event_codec/tests/knowledge_manifest.rs b/crates/event_codec/tests/knowledge_manifest.rs @@ -1,5 +1,7 @@ #![cfg(all(feature = "knowledge", feature = "manifests"))] +mod support; + use std::collections::BTreeSet; use radroots_event::contract::VERSION; @@ -17,7 +19,7 @@ use radroots_event_codec::manifest::{ RADROOTS_KNOWLEDGE_CONTRACT_MANIFEST_SCHEMA_VERSION, RadrootsKnowledgeContractManifest, contract_manifest_json, contract_manifest_sha256, knowledge_contract_manifest, }; -use radroots_test_fixtures::knowledge::RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS; +use support::knowledge::RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS; const MANIFEST_JSON: &str = include_str!("../../../contracts/knowledge/knowledge_event_contract_manifest.v2.json"); diff --git a/crates/event_codec/tests/nip09_suppression_conformance.rs b/crates/event_codec/tests/nip09_suppression_conformance.rs @@ -1,5 +1,7 @@ #![cfg(feature = "json")] +mod support; + use std::{ borrow::Cow, collections::{BTreeMap, BTreeSet}, @@ -534,7 +536,7 @@ fn is_forbidden_metadata_key(key: &str) -> bool { fn contains_approved_fixture_secret(value: &str) -> bool { let normalized = value.to_ascii_lowercase(); - radroots_test_fixtures::approved_fixture_identities() + support::approved_fixture_identities() .iter() .any(|identity| normalized.contains(identity.secret_key_hex)) } @@ -555,7 +557,7 @@ fn suppression_corpus_hygiene_rejects_generation_and_effect_authority_metadata() assert!(error.contains(key), "{error}"); } assert!(validate_no_forbidden_corpus_metadata(&json!("NSEC1FORBIDDEN"), "$").is_err()); - for identity in radroots_test_fixtures::approved_fixture_identities() { + for identity in support::approved_fixture_identities() { assert!( validate_no_forbidden_corpus_metadata(&json!(identity.secret_key_hex), "$").is_err() ); diff --git a/crates/event_codec/tests/package_boundary.rs b/crates/event_codec/tests/package_boundary.rs @@ -17,7 +17,7 @@ const PUBLIC_API: &str = include_str!("../../../docs/api/radroots_event_codec.tx fn manifest_has_final_identity_and_required_radroots_dependencies() { assert!(MANIFEST.contains("name = \"radroots_event_codec\"")); assert!(MANIFEST.contains("version = \"0.1.0-alpha\"")); - assert!(MANIFEST.contains("publish = false")); + assert!(MANIFEST.contains("publish = [\"crates-io\"]")); assert!(MANIFEST.contains("[lib]\nname = \"radroots_event_codec\"")); let dependencies = table_keys(MANIFEST, "[dependencies]"); @@ -95,7 +95,7 @@ fn canonical_root_exports_are_explicit_and_host_types_do_not_leak() { #[test] fn compatibility_surface_is_private_documentation_hidden_and_has_a_removal_gate() { - assert!(MANIFEST.contains("publish = false")); + assert!(MANIFEST.contains("publish = [\"crates-io\"]")); for module in [ "comment", "deletion", diff --git a/crates/event_codec/tests/support/knowledge.rs b/crates/event_codec/tests/support/knowledge.rs @@ -0,0 +1,521 @@ +use radroots_event::envelope::kind::{ + KIND_CONTRIBUTION_ATTESTATION, KIND_EVIDENCE_BOUNTY, KIND_FILE_METADATA, + KIND_KNOWLEDGE_CHANGE_PROPOSAL, KIND_KNOWLEDGE_CLAIM, KIND_KNOWLEDGE_FIELD_REPORT, + KIND_KNOWLEDGE_RELATION, KIND_KNOWLEDGE_REVIEW, KIND_KNOWLEDGE_SOURCE, KIND_WIKI_ARTICLE, + KIND_WIKI_MERGE_REQUEST, KIND_WIKI_REDIRECT, +}; +use radroots_event::knowledge::{ + AddressableRef, ContributionAttestation, EvidenceBounty, KnowledgeChangeProposal, + KnowledgeCitationSpan, KnowledgeClaim, KnowledgeFieldContext, KnowledgeFieldReport, + KnowledgeLocation, KnowledgeLocationPrecision, KnowledgeNodeRef, KnowledgeObservation, + KnowledgeObservationValue, KnowledgeRelation, KnowledgeReview, KnowledgeReviewScope, + KnowledgeReviewScore, KnowledgeReviewTarget, KnowledgeSource, + RADROOTS_CONTRIBUTION_ATTESTATION_SCHEMA, RADROOTS_EVIDENCE_BOUNTY_SCHEMA, + RADROOTS_KNOWLEDGE_CHANGE_PROPOSAL_SCHEMA, RADROOTS_KNOWLEDGE_CLAIM_SCHEMA, + RADROOTS_KNOWLEDGE_FIELD_REPORT_SCHEMA, RADROOTS_KNOWLEDGE_RELATION_SCHEMA, + RADROOTS_KNOWLEDGE_REVIEW_SCHEMA, RADROOTS_KNOWLEDGE_SCHEMA_VERSION, + RADROOTS_KNOWLEDGE_SOURCE_SCHEMA, WikiArticle, WikiArticleVersionRef, WikiMergeRequest, + WikiRedirect, +}; +use radroots_event::tag::EventRef; +use radroots_identity::PublicKey; + +use super::{FIXTURE_ALICE_PUBLIC_KEY_HEX, RELAY_PRIMARY_WSS}; + +pub const RADROOTS_KNOWLEDGE_FIXTURE_NAMESPACE: &str = "radroots-knowledge-fixture-v1"; + +pub const RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS: [&str; 11] = [ + "radroots.wiki.article.v1", + "radroots.wiki.redirect.v1", + "radroots.wiki.merge_request.v1", + RADROOTS_KNOWLEDGE_SOURCE_SCHEMA, + RADROOTS_KNOWLEDGE_CLAIM_SCHEMA, + RADROOTS_KNOWLEDGE_RELATION_SCHEMA, + RADROOTS_KNOWLEDGE_REVIEW_SCHEMA, + RADROOTS_KNOWLEDGE_FIELD_REPORT_SCHEMA, + RADROOTS_EVIDENCE_BOUNTY_SCHEMA, + RADROOTS_KNOWLEDGE_CHANGE_PROPOSAL_SCHEMA, + RADROOTS_CONTRIBUTION_ATTESTATION_SCHEMA, +]; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct RadrootsKnowledgeAdversarialFixture { + pub id: &'static str, + pub pipeline_stage: &'static str, + pub expected_error_code: &'static str, +} + +pub const RADROOTS_KNOWLEDGE_ADVERSARIAL_FIXTURES: [RadrootsKnowledgeAdversarialFixture; 13] = [ + RadrootsKnowledgeAdversarialFixture { + id: "malformed_tags", + pipeline_stage: "contract_validation", + expected_error_code: "tag_cardinality_mismatch", + }, + RadrootsKnowledgeAdversarialFixture { + id: "wrong_schema", + pipeline_stage: "contract_validation", + expected_error_code: "content_field_mismatch", + }, + RadrootsKnowledgeAdversarialFixture { + id: "missing_contract_id", + pipeline_stage: "contract_validation", + expected_error_code: "contract_match", + }, + RadrootsKnowledgeAdversarialFixture { + id: "private_coordinate_leakage", + pipeline_stage: "event_parse", + expected_error_code: "invalid_json", + }, + RadrootsKnowledgeAdversarialFixture { + id: "unsupported_contract_shape", + pipeline_stage: "contract_validation", + expected_error_code: "contract_match", + }, + RadrootsKnowledgeAdversarialFixture { + id: "invalid_nip54_d_tag", + pipeline_stage: "encode", + expected_error_code: "invalid_field", + }, + RadrootsKnowledgeAdversarialFixture { + id: "invalid_redirect_target_kind", + pipeline_stage: "encode", + expected_error_code: "invalid_field", + }, + RadrootsKnowledgeAdversarialFixture { + id: "merge_request_missing_source_marker", + pipeline_stage: "event_parse", + expected_error_code: "invalid_tag", + }, + RadrootsKnowledgeAdversarialFixture { + id: "merge_request_json_content_guard", + pipeline_stage: "wire_shape", + expected_error_code: "plain_text_content", + }, + RadrootsKnowledgeAdversarialFixture { + id: "orphan_fork_marker", + pipeline_stage: "event_parse", + expected_error_code: "invalid_tag", + }, + RadrootsKnowledgeAdversarialFixture { + id: "orphan_defer_marker", + pipeline_stage: "event_parse", + expected_error_code: "invalid_tag", + }, + RadrootsKnowledgeAdversarialFixture { + id: "id_mismatch", + pipeline_stage: "nip01_verification", + expected_error_code: "id_mismatch", + }, + RadrootsKnowledgeAdversarialFixture { + id: "signature_invalidity", + pipeline_stage: "nip01_verification", + expected_error_code: "signature_invalid", + }, +]; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RadrootsKnowledgeFixture { + WikiArticle(WikiArticle), + WikiRedirect(WikiRedirect), + WikiMergeRequest(WikiMergeRequest), + KnowledgeSource(KnowledgeSource), + KnowledgeClaim(KnowledgeClaim), + KnowledgeRelation(KnowledgeRelation), + KnowledgeReview(KnowledgeReview), + KnowledgeFieldReport(KnowledgeFieldReport), + EvidenceBounty(EvidenceBounty), + KnowledgeChangeProposal(KnowledgeChangeProposal), + ContributionAttestation(ContributionAttestation), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RadrootsKnowledgeFixtureCase { + pub id: &'static str, + pub contract_id: &'static str, + pub kind: u32, + pub data: RadrootsKnowledgeFixture, +} + +pub fn hex_64(character: char) -> String { + core::iter::repeat_n(character, 64).collect() +} + +pub fn event_ref(character: char, kind: u32) -> EventRef { + EventRef { + id: hex_64(character), + author: PublicKey::from_hex(FIXTURE_ALICE_PUBLIC_KEY_HEX) + .expect("fixture public key is valid"), + kind, + d_tag: None, + relays: Some(vec![RELAY_PRIMARY_WSS.to_string()]), + } +} + +pub fn address_ref() -> AddressableRef { + AddressableRef { + kind: KIND_WIKI_ARTICLE, + pubkey: hex_64('a'), + d_tag: "soil-health".to_string(), + relays: vec![RELAY_PRIMARY_WSS.to_string()], + } +} + +pub fn deferred_address_ref() -> AddressableRef { + AddressableRef { + kind: KIND_WIKI_ARTICLE, + pubkey: hex_64('a'), + d_tag: "soil-health-v2".to_string(), + relays: vec![RELAY_PRIMARY_WSS.to_string()], + } +} + +pub fn wiki_article_version_ref() -> WikiArticleVersionRef { + WikiArticleVersionRef { + event_id: hex_64('b'), + address_ref: address_ref(), + } +} + +pub fn wiki_article_deferred_version_ref() -> WikiArticleVersionRef { + WikiArticleVersionRef { + event_id: hex_64('c'), + address_ref: deferred_address_ref(), + } +} + +pub fn wiki_article() -> WikiArticle { + WikiArticle { + d_tag: "soil-health".to_string(), + title: Some("Soil health".to_string()), + content_djot: "# Soil health\n\nLiving soil supports resilient local food systems." + .to_string(), + summary: Some("Living soil basics".to_string()), + topics: vec!["soil".to_string(), "local-food".to_string()], + references: vec![event_ref('2', KIND_KNOWLEDGE_SOURCE)], + forked_from: vec![wiki_article_version_ref()], + deferred_to: Some(wiki_article_deferred_version_ref()), + } +} + +pub fn wiki_redirect() -> WikiRedirect { + WikiRedirect { + d_tag: "soil".to_string(), + target: address_ref(), + } +} + +pub fn wiki_merge_request() -> WikiMergeRequest { + WikiMergeRequest { + target_article: address_ref(), + destination_pubkey: hex_64('a'), + base_version_event_id: Some(hex_64('e')), + source_version_event_id: hex_64('f'), + explanation: Some("Merge synthetic soil article updates".to_string()), + } +} + +pub fn wiki_merge_request_without_base_version() -> WikiMergeRequest { + WikiMergeRequest { + base_version_event_id: None, + ..wiki_merge_request() + } +} + +pub fn knowledge_source() -> KnowledgeSource { + KnowledgeSource { + schema: RADROOTS_KNOWLEDGE_SOURCE_SCHEMA.to_string(), + schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION, + d_tag: "soil-source".to_string(), + title: "Soil Source".to_string(), + source_type: "book".to_string(), + authors: vec!["A. Example".to_string()], + publisher: Some("Radroots Synthetic Press".to_string()), + publication_year: Some(2026), + edition: None, + canonical_url: Some("https://source.example.test/soil-source".to_string()), + artifact_refs: vec![event_ref('3', KIND_FILE_METADATA)], + author_asserted_rights: None, + topics: vec!["soil".to_string()], + summary: Some("Synthetic source for knowledge fixture coverage".to_string()), + } +} + +pub fn knowledge_claim() -> KnowledgeClaim { + KnowledgeClaim { + schema: RADROOTS_KNOWLEDGE_CLAIM_SCHEMA.to_string(), + schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION, + claim_type: "practice_effect".to_string(), + text: "Cover crops improve soil structure.".to_string(), + citation_spans: vec![KnowledgeCitationSpan { + source_ref: event_ref('4', KIND_KNOWLEDGE_SOURCE), + artifact_ref: None, + page_start: Some(12), + page_end: Some(13), + section_path: vec!["chapter-1".to_string()], + quote_hash: Some(hex_64('5')), + chunk_id: Some("chunk-1".to_string()), + }], + topics: vec!["cover-crops".to_string()], + applies_to: vec!["local-food".to_string()], + author_asserted_confidence: Some("medium".to_string()), + supersedes: Vec::new(), + } +} + +pub fn knowledge_node_ref(label: &str) -> KnowledgeNodeRef { + KnowledgeNodeRef { + node_type: "event".to_string(), + event_ref: Some(event_ref('6', KIND_KNOWLEDGE_CLAIM)), + address_ref: None, + external_id: None, + label: Some(label.to_string()), + } +} + +pub fn knowledge_relation() -> KnowledgeRelation { + KnowledgeRelation { + schema: RADROOTS_KNOWLEDGE_RELATION_SCHEMA.to_string(), + schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION, + subject: knowledge_node_ref("cover crops"), + predicate: "supports".to_string(), + object: knowledge_node_ref("soil structure"), + support_refs: vec![event_ref('7', KIND_KNOWLEDGE_CLAIM)], + author_asserted_confidence: Some("medium".to_string()), + supersedes: Vec::new(), + } +} + +pub fn knowledge_review() -> KnowledgeReview { + KnowledgeReview { + schema: RADROOTS_KNOWLEDGE_REVIEW_SCHEMA.to_string(), + schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION, + target: KnowledgeReviewTarget { + event_id: hex_64('8'), + author_pubkey: hex_64('a'), + kind: KIND_KNOWLEDGE_CLAIM, + address: None, + relays: vec![RELAY_PRIMARY_WSS.to_string()], + review_scope: KnowledgeReviewScope::SpecificVersion, + }, + reviewer_role: "peer".to_string(), + verdict: "needs_more_evidence".to_string(), + scores: vec![KnowledgeReviewScore { + dimension: "evidence".to_string(), + value: "partial".to_string(), + note: None, + }], + notes: Some("Synthetic review".to_string()), + evidence_refs: vec![event_ref('9', KIND_KNOWLEDGE_SOURCE)], + } +} + +pub fn knowledge_field_report() -> KnowledgeFieldReport { + KnowledgeFieldReport { + schema: RADROOTS_KNOWLEDGE_FIELD_REPORT_SCHEMA.to_string(), + schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION, + report_type: "observation".to_string(), + title: "Field observation".to_string(), + summary: Some("Observed cover crop residue.".to_string()), + context: KnowledgeFieldContext { + location_precision: KnowledgeLocationPrecision::CoarseGeohash, + public_location: Some(KnowledgeLocation { + label: Some("watershed".to_string()), + region: Some("synthetic-region".to_string()), + locality: None, + geohash: Some("c23".to_string()), + }), + private_location_ref: None, + topics: vec!["field".to_string()], + context_tags: vec!["observation".to_string()], + }, + observations: vec![KnowledgeObservation { + observation_type: "residue".to_string(), + text: "Residue was visible across beds.".to_string(), + observed_at: Some("2026-07-05".to_string()), + values: vec![KnowledgeObservationValue { + key: "coverage".to_string(), + value: "medium".to_string(), + unit: None, + }], + }], + artifact_refs: vec![event_ref('c', KIND_FILE_METADATA)], + related_refs: vec![event_ref('d', KIND_KNOWLEDGE_CLAIM)], + limitations: vec!["single observer".to_string()], + } +} + +pub fn evidence_bounty() -> EvidenceBounty { + EvidenceBounty { + schema: RADROOTS_EVIDENCE_BOUNTY_SCHEMA.to_string(), + schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION, + d_tag: "soil-bounty".to_string(), + title: "Soil bounty".to_string(), + summary: None, + topics: vec!["soil".to_string()], + target_refs: vec![event_ref('a', KIND_KNOWLEDGE_CLAIM)], + reward_note: None, + closes_at: None, + } +} + +pub fn knowledge_change_proposal() -> KnowledgeChangeProposal { + KnowledgeChangeProposal { + schema: RADROOTS_KNOWLEDGE_CHANGE_PROPOSAL_SCHEMA.to_string(), + schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION, + target: event_ref('b', KIND_KNOWLEDGE_CLAIM), + proposal_type: "amend".to_string(), + summary: "Clarify scope".to_string(), + rationale: None, + evidence_refs: vec![event_ref('c', KIND_KNOWLEDGE_SOURCE)], + supersedes: Vec::new(), + } +} + +pub fn contribution_attestation() -> ContributionAttestation { + ContributionAttestation { + schema: RADROOTS_CONTRIBUTION_ATTESTATION_SCHEMA.to_string(), + schema_version: RADROOTS_KNOWLEDGE_SCHEMA_VERSION, + contributor_pubkey: hex_64('a'), + contribution_type: "review".to_string(), + subject_refs: vec![event_ref('d', KIND_KNOWLEDGE_REVIEW)], + summary: "Reviewed synthetic claim".to_string(), + evidence_refs: vec![event_ref('e', KIND_KNOWLEDGE_REVIEW)], + } +} + +pub fn knowledge_valid_fixtures() -> Vec<RadrootsKnowledgeFixtureCase> { + vec![ + RadrootsKnowledgeFixtureCase { + id: "wiki_article_valid", + contract_id: "radroots.wiki.article.v1", + kind: KIND_WIKI_ARTICLE, + data: RadrootsKnowledgeFixture::WikiArticle(wiki_article()), + }, + RadrootsKnowledgeFixtureCase { + id: "wiki_redirect_valid", + contract_id: "radroots.wiki.redirect.v1", + kind: KIND_WIKI_REDIRECT, + data: RadrootsKnowledgeFixture::WikiRedirect(wiki_redirect()), + }, + RadrootsKnowledgeFixtureCase { + id: "wiki_merge_request_valid", + contract_id: "radroots.wiki.merge_request.v1", + kind: KIND_WIKI_MERGE_REQUEST, + data: RadrootsKnowledgeFixture::WikiMergeRequest(wiki_merge_request()), + }, + RadrootsKnowledgeFixtureCase { + id: "wiki_merge_request_without_base_valid", + contract_id: "radroots.wiki.merge_request.v1", + kind: KIND_WIKI_MERGE_REQUEST, + data: RadrootsKnowledgeFixture::WikiMergeRequest( + wiki_merge_request_without_base_version(), + ), + }, + RadrootsKnowledgeFixtureCase { + id: "knowledge_source_valid", + contract_id: RADROOTS_KNOWLEDGE_SOURCE_SCHEMA, + kind: KIND_KNOWLEDGE_SOURCE, + data: RadrootsKnowledgeFixture::KnowledgeSource(knowledge_source()), + }, + RadrootsKnowledgeFixtureCase { + id: "knowledge_claim_valid", + contract_id: RADROOTS_KNOWLEDGE_CLAIM_SCHEMA, + kind: KIND_KNOWLEDGE_CLAIM, + data: RadrootsKnowledgeFixture::KnowledgeClaim(knowledge_claim()), + }, + RadrootsKnowledgeFixtureCase { + id: "knowledge_relation_valid", + contract_id: RADROOTS_KNOWLEDGE_RELATION_SCHEMA, + kind: KIND_KNOWLEDGE_RELATION, + data: RadrootsKnowledgeFixture::KnowledgeRelation(knowledge_relation()), + }, + RadrootsKnowledgeFixtureCase { + id: "knowledge_review_valid", + contract_id: RADROOTS_KNOWLEDGE_REVIEW_SCHEMA, + kind: KIND_KNOWLEDGE_REVIEW, + data: RadrootsKnowledgeFixture::KnowledgeReview(knowledge_review()), + }, + RadrootsKnowledgeFixtureCase { + id: "knowledge_field_report_valid", + contract_id: RADROOTS_KNOWLEDGE_FIELD_REPORT_SCHEMA, + kind: KIND_KNOWLEDGE_FIELD_REPORT, + data: RadrootsKnowledgeFixture::KnowledgeFieldReport(knowledge_field_report()), + }, + RadrootsKnowledgeFixtureCase { + id: "evidence_bounty_valid", + contract_id: RADROOTS_EVIDENCE_BOUNTY_SCHEMA, + kind: KIND_EVIDENCE_BOUNTY, + data: RadrootsKnowledgeFixture::EvidenceBounty(evidence_bounty()), + }, + RadrootsKnowledgeFixtureCase { + id: "knowledge_change_proposal_valid", + contract_id: RADROOTS_KNOWLEDGE_CHANGE_PROPOSAL_SCHEMA, + kind: KIND_KNOWLEDGE_CHANGE_PROPOSAL, + data: RadrootsKnowledgeFixture::KnowledgeChangeProposal(knowledge_change_proposal()), + }, + RadrootsKnowledgeFixtureCase { + id: "contribution_attestation_valid", + contract_id: RADROOTS_CONTRIBUTION_ATTESTATION_SCHEMA, + kind: KIND_CONTRIBUTION_ATTESTATION, + data: RadrootsKnowledgeFixture::ContributionAttestation(contribution_attestation()), + }, + ] +} + +#[cfg(test)] +#[cfg_attr(coverage_nightly, coverage(off))] +mod tests { + use super::{ + RADROOTS_KNOWLEDGE_ADVERSARIAL_FIXTURES, RADROOTS_KNOWLEDGE_FIXTURE_NAMESPACE, + RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS, knowledge_valid_fixtures, + }; + + #[test] + fn valid_fixture_catalog_covers_all_contract_ids() { + let fixtures = knowledge_valid_fixtures(); + assert!(fixtures.len() >= RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS.len()); + for contract_id in RADROOTS_KNOWLEDGE_VALID_CONTRACT_IDS { + assert!( + fixtures + .iter() + .any(|fixture| fixture.contract_id == contract_id), + "{contract_id}" + ); + } + assert_eq!( + RADROOTS_KNOWLEDGE_FIXTURE_NAMESPACE, + "radroots-knowledge-fixture-v1" + ); + } + + #[test] + fn adversarial_fixture_catalog_covers_required_cases() { + let expected = [ + "malformed_tags", + "wrong_schema", + "missing_contract_id", + "private_coordinate_leakage", + "unsupported_contract_shape", + "invalid_nip54_d_tag", + "invalid_redirect_target_kind", + "merge_request_missing_source_marker", + "merge_request_json_content_guard", + "orphan_fork_marker", + "orphan_defer_marker", + "id_mismatch", + "signature_invalidity", + ]; + assert_eq!( + RADROOTS_KNOWLEDGE_ADVERSARIAL_FIXTURES.len(), + expected.len() + ); + for id in expected { + assert!( + RADROOTS_KNOWLEDGE_ADVERSARIAL_FIXTURES + .iter() + .any(|fixture| fixture.id == id), + "{id}" + ); + } + } +} diff --git a/crates/event_codec/tests/support/mod.rs b/crates/event_codec/tests/support/mod.rs @@ -0,0 +1,133 @@ +#![forbid(unsafe_code)] +#![allow(dead_code)] + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ApprovedFixtureIdentity { + pub label: &'static str, + pub username: &'static str, + pub email: &'static str, + pub secret_key_hex: &'static str, + pub public_key_hex: &'static str, + pub nsec: &'static str, + pub npub: &'static str, +} + +pub const APPROVED_FIXTURE_NAMESPACE: &str = "radroots-approved-fixture-v1"; + +pub const FIXTURE_ALICE_LABEL: &str = "fixture_alice"; +pub const FIXTURE_ALICE_USERNAME: &str = "fixture_alice"; +pub const FIXTURE_ALICE_EMAIL: &str = "fixture_alice@fixtures.test"; +pub const FIXTURE_ALICE_SECRET_KEY_HEX: &str = + "10c5304d6c9ae3a1a16f7860f1cc8f5e3a76225a2663b3a989a0d775919b7df5"; +pub const FIXTURE_ALICE_PUBLIC_KEY_HEX: &str = + "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df"; +pub const FIXTURE_ALICE_NSEC: &str = + "nsec1zrznqntvnt36rgt00ps0rny0tca8vgj6ye3m82vf5rthtyvm0h6syu7drz"; +pub const FIXTURE_ALICE_NPUB: &str = + "npub1tp2ez55a5zatxxemrv0eses3ea05xhw2snuh3jy7azjqejn3q00s3vy5a9"; +pub const FIXTURE_ALICE: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_ALICE_LABEL, + username: FIXTURE_ALICE_USERNAME, + email: FIXTURE_ALICE_EMAIL, + secret_key_hex: FIXTURE_ALICE_SECRET_KEY_HEX, + public_key_hex: FIXTURE_ALICE_PUBLIC_KEY_HEX, + nsec: FIXTURE_ALICE_NSEC, + npub: FIXTURE_ALICE_NPUB, +}; + +pub const FIXTURE_BOB_LABEL: &str = "fixture_bob"; +pub const FIXTURE_BOB_USERNAME: &str = "fixture_bob"; +pub const FIXTURE_BOB_EMAIL: &str = "fixture_bob@fixtures.test"; +pub const FIXTURE_BOB_SECRET_KEY_HEX: &str = + "59392e9068f66431b12f70218fb61281cb6b433d7f27c55d61f1a63fe1a96ff8"; +pub const FIXTURE_BOB_PUBLIC_KEY_HEX: &str = + "e0266e3cfb0d2886f91c73f5f868f3b98273713e5fcd97c081663f5518a4b3af"; +pub const FIXTURE_BOB_NSEC: &str = + "nsec1tyujayrg7ejrrvf0wqscldsjs89kksea0unu2htp7xnrlcdfdluqrjya9h"; +pub const FIXTURE_BOB_NPUB: &str = + "npub1uqnxu08mp55gd7guw06ls68nhxp8xuf7tlxe0sypvcl42x9ykwhsd55k2g"; +pub const FIXTURE_BOB: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_BOB_LABEL, + username: FIXTURE_BOB_USERNAME, + email: FIXTURE_BOB_EMAIL, + secret_key_hex: FIXTURE_BOB_SECRET_KEY_HEX, + public_key_hex: FIXTURE_BOB_PUBLIC_KEY_HEX, + nsec: FIXTURE_BOB_NSEC, + npub: FIXTURE_BOB_NPUB, +}; + +pub const FIXTURE_CAROL_LABEL: &str = "fixture_carol"; +pub const FIXTURE_CAROL_USERNAME: &str = "fixture_carol"; +pub const FIXTURE_CAROL_EMAIL: &str = "fixture_carol@fixtures.test"; +pub const FIXTURE_CAROL_SECRET_KEY_HEX: &str = + "4d6c20fdd86857de77ff5cfa5c545751ba2efd126e0b6642dae9764d782d6509"; +pub const FIXTURE_CAROL_PUBLIC_KEY_HEX: &str = + "1952b8c6943898bceffcff1b7699c4a775a4d13b4a9ba0096ba26ef04492bb1c"; +pub const FIXTURE_CAROL_NSEC: &str = + "nsec1f4kzplwcdptaualltna9c4zh2xazalgjdc9kvsk6a9my67pdv5ys2pqkaj"; +pub const FIXTURE_CAROL_NPUB: &str = + "npub1r9ft33558zvtemluludhdxwy5a66f5fmf2d6qztt5fh0q3yjhvwqgzmkl6"; +pub const FIXTURE_CAROL: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_CAROL_LABEL, + username: FIXTURE_CAROL_USERNAME, + email: FIXTURE_CAROL_EMAIL, + secret_key_hex: FIXTURE_CAROL_SECRET_KEY_HEX, + public_key_hex: FIXTURE_CAROL_PUBLIC_KEY_HEX, + nsec: FIXTURE_CAROL_NSEC, + npub: FIXTURE_CAROL_NPUB, +}; + +pub const FIXTURE_DIEGO_LABEL: &str = "fixture_diego"; +pub const FIXTURE_DIEGO_USERNAME: &str = "fixture_diego"; +pub const FIXTURE_DIEGO_EMAIL: &str = "fixture_diego@fixtures.test"; +pub const FIXTURE_DIEGO_SECRET_KEY_HEX: &str = + "9de56c1fdfce9ab00af85b3d7003c1d15cffb84cdf303c3a83c1a3fb1a2d0db0"; +pub const FIXTURE_DIEGO_PUBLIC_KEY_HEX: &str = + "5d3eab6e78eb7e467a9e196a63456c9fafb93fb88b7052b83229870889923aa4"; +pub const FIXTURE_DIEGO_NSEC: &str = + "nsec1nhjkc87le6dtqzhctv7hqq7p69w0lwzvmucrcw5rcx3lkx3dpkcqkrmgp5"; +pub const FIXTURE_DIEGO_NPUB: &str = + "npub1t5l2kmncadlyv757r94xx3tvn7hmj0ac3dc99wpj9xrs3zvj82jqwwcglm"; +pub const FIXTURE_DIEGO: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_DIEGO_LABEL, + username: FIXTURE_DIEGO_USERNAME, + email: FIXTURE_DIEGO_EMAIL, + secret_key_hex: FIXTURE_DIEGO_SECRET_KEY_HEX, + public_key_hex: FIXTURE_DIEGO_PUBLIC_KEY_HEX, + nsec: FIXTURE_DIEGO_NSEC, + npub: FIXTURE_DIEGO_NPUB, +}; + +pub const APPROVED_FIXTURE_IDENTITIES: [ApprovedFixtureIdentity; 4] = + [FIXTURE_ALICE, FIXTURE_BOB, FIXTURE_CAROL, FIXTURE_DIEGO]; + +pub const fn approved_fixture_identities() -> &'static [ApprovedFixtureIdentity; 4] { + &APPROVED_FIXTURE_IDENTITIES +} + +pub fn fixture_public_key_hex(character: char) -> String { + let canonical = match character.to_ascii_lowercase() { + 'a' => FIXTURE_ALICE_PUBLIC_KEY_HEX, + 'b' => FIXTURE_BOB_PUBLIC_KEY_HEX, + 'c' => FIXTURE_CAROL_PUBLIC_KEY_HEX, + 'd' => FIXTURE_DIEGO_PUBLIC_KEY_HEX, + 'e' => "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + 'f' => "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9", + _ => panic!("unsupported fixture public-key label: {character}"), + }; + if character.is_ascii_uppercase() { + canonical.to_ascii_uppercase() + } else { + canonical.to_owned() + } +} + +pub const RELAY_PRIMARY_WSS: &str = "wss://relay.example.com"; +pub const RELAY_SECONDARY_WSS: &str = "wss://relay-2.example.com"; +pub const RELAY_TERTIARY_WSS: &str = "wss://relay-3.example.com"; + +pub const APP_PRIMARY_HTTPS: &str = "https://app.example.com"; +pub const API_PRIMARY_HTTPS: &str = "https://api.example.com"; +pub const CDN_PRIMARY_HTTPS: &str = "https://cdn.example.com"; + +pub mod knowledge; diff --git a/crates/event_codec/tests/verified_deletion_conformance.rs b/crates/event_codec/tests/verified_deletion_conformance.rs @@ -1,5 +1,7 @@ #![cfg(feature = "json")] +mod support; + use std::{ borrow::Cow, collections::{BTreeMap, BTreeSet}, @@ -383,7 +385,7 @@ fn contains_nsec_material(value: &str) -> bool { fn contains_approved_fixture_secret(value: &str) -> bool { let normalized = value.to_ascii_lowercase(); - radroots_test_fixtures::approved_fixture_identities() + support::approved_fixture_identities() .iter() .any(|identity| normalized.contains(identity.secret_key_hex)) } @@ -406,7 +408,7 @@ fn deletion_corpus_hygiene_recognizes_every_approved_fixture_secret() { assert!(error.contains(key), "{error}"); } - for identity in radroots_test_fixtures::approved_fixture_identities() { + for identity in support::approved_fixture_identities() { assert!(contains_approved_fixture_secret(identity.secret_key_hex)); assert!(contains_approved_fixture_secret( format!("prefix{}suffix", identity.secret_key_hex).as_str() diff --git a/crates/geonames/tests/package_boundary.rs b/crates/geonames/tests/package_boundary.rs @@ -19,7 +19,7 @@ fn manifest_and_root_match_the_governed_provider_boundary() { for required in [ "name = \"radroots_geonames\"", "version = \"0.1.0-alpha\"", - "publish = false", + "publish = [\"crates-io\"]", "[lib]\nname = \"radroots_geonames\"", ] { assert!( diff --git a/crates/nostr/Cargo.toml b/crates/nostr/Cargo.toml @@ -68,7 +68,6 @@ radroots_blossom = { workspace = true, default-features = false, features = [ radroots_protocol = { workspace = true, default-features = false, features = [ "std", ] } -radroots_test_fixtures = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } [[test]] diff --git a/crates/nostr/tests/food_availability_profile.rs b/crates/nostr/tests/food_availability_profile.rs @@ -1,3 +1,5 @@ +mod support; + use nostr::{Keys as RadrootsNostrKeys, SecretKey as RadrootsNostrSecretKey}; use radroots_blossom::{BlobDescriptor, BlobUrl, MediaType, Sha256}; use radroots_event::food::availability::{ @@ -16,7 +18,7 @@ use radroots_nostr::event::Timestamp as RadrootsNostrTimestamp; use radroots_nostr::event::build_food_availability as build_food_availability_event; use radroots_nostr::event::from_nostr; use radroots_nostr::tag::Tag as RadrootsNostrTag; -use radroots_test_fixtures::FIXTURE_ALICE_SECRET_KEY_HEX; +use support::FIXTURE_ALICE_SECRET_KEY_HEX; const CREATED_AT: u64 = 1_784_347_200; diff --git a/crates/nostr/tests/nip09_deletion_request_profile.rs b/crates/nostr/tests/nip09_deletion_request_profile.rs @@ -1,3 +1,5 @@ +mod support; + use nostr::{Keys as RadrootsNostrKeys, SecretKey as RadrootsNostrSecretKey}; use radroots_event::{ envelope::kind::KIND_DELETION_REQUEST, @@ -13,7 +15,7 @@ use radroots_nostr::event::from_nostr; use radroots_nostr::event::{ GenericBuilder, build_nip09_deletion_request as build_nip09_deletion_request_event, }; -use radroots_test_fixtures::{FIXTURE_ALICE_SECRET_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX}; +use support::{FIXTURE_ALICE_SECRET_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX}; const CREATED_AT: u64 = 1_784_347_200; const TARGET_EVENT_ID: &str = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; diff --git a/crates/nostr/tests/nip10_reply_profile.rs b/crates/nostr/tests/nip10_reply_profile.rs @@ -1,3 +1,5 @@ +mod support; + use nostr::{Keys as RadrootsNostrKeys, SecretKey as RadrootsNostrSecretKey}; use radroots_event::post::reply::{AuthoredNip10Reply, Nip10ReplyReference}; use radroots_event_codec::{ @@ -14,7 +16,7 @@ use radroots_nostr::event::Timestamp as RadrootsNostrTimestamp; use radroots_nostr::event::build_nip10_reply as build_nip10_reply_event; use radroots_nostr::event::from_nostr; use radroots_nostr::tag::Tag as RadrootsNostrTag; -use radroots_test_fixtures::{ +use support::{ FIXTURE_ALICE_SECRET_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX, FIXTURE_CAROL_PUBLIC_KEY_HEX, RELAY_PRIMARY_WSS, RELAY_SECONDARY_WSS, }; diff --git a/crates/nostr/tests/nip17_conformance.rs b/crates/nostr/tests/nip17_conformance.rs @@ -1,18 +1,20 @@ #![cfg(feature = "nip17")] +mod support; + use nostr::{Keys, SecretKey}; use radroots_event::social::message::{Message, MessageRecipient}; use radroots_event::social::message_file::{MessageFile, MessageFileDimensions}; use radroots_nostr::nip17::{ Rumor, WrapOptions, unwrap_gift_wrap, wrap_message, wrap_message_file, }; -use radroots_test_fixtures::{ - FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_ALICE_SECRET_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX, - FIXTURE_BOB_SECRET_KEY_HEX, -}; use serde::Deserialize; use serde_json::Value; use std::{borrow::Cow, fs, path::Path}; +use support::{ + FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_ALICE_SECRET_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX, + FIXTURE_BOB_SECRET_KEY_HEX, +}; const PACKAGED_VECTORS: &str = include_str!("fixtures/nip17_adapter.v1.json"); const WORKSPACE_VECTOR_PATH: &str = "../../contracts/conformance/vectors/nip17/adapter.v1.json"; diff --git a/crates/nostr/tests/nip22_comment_profile.rs b/crates/nostr/tests/nip22_comment_profile.rs @@ -1,3 +1,5 @@ +mod support; + use nostr::{Keys as RadrootsNostrKeys, SecretKey as RadrootsNostrSecretKey}; use radroots_event::{ envelope::kind::{ @@ -17,7 +19,7 @@ use radroots_nostr::event::Kind as RadrootsNostrKind; use radroots_nostr::event::Timestamp as RadrootsNostrTimestamp; use radroots_nostr::event::from_nostr; use radroots_nostr::event::{GenericBuilder, build_nip22_comment as build_nip22_comment_event}; -use radroots_test_fixtures::{ +use support::{ FIXTURE_ALICE_SECRET_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX, FIXTURE_CAROL_PUBLIC_KEY_HEX, RELAY_PRIMARY_WSS, RELAY_SECONDARY_WSS, }; diff --git a/crates/nostr/tests/package_boundary.rs b/crates/nostr/tests/package_boundary.rs @@ -34,7 +34,7 @@ fn manifest_has_final_identity_features_and_radroots_dependencies() { for required in [ "name = \"radroots_nostr\"", "version = \"0.1.0-alpha\"", - "publish = false", + "publish = [\"crates-io\"]", "[lib]\nname = \"radroots_nostr\"", "default = [\"std\", \"events\"]", "radroots_identity = { workspace = true, default-features = false }", @@ -552,12 +552,12 @@ fn superseded_surface_retirement_is_explicit_and_release_bounded() { } } for required in [ - "publish = false", + "publish = [\"crates-io\"]", "outside the authorized `oss/lib` and `oss/sdk` crate-surface edit boundary", "no compatibility path was restored", "Step 313 is the exact final pre-release audit", ] { - let authority = if required == "publish = false" { + let authority = if required == "publish = [\"crates-io\"]" { MANIFEST } else { COMPATIBILITY diff --git a/crates/nostr/tests/support.rs b/crates/nostr/tests/support.rs @@ -0,0 +1,107 @@ +#![forbid(unsafe_code)] +#![allow(dead_code)] + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ApprovedFixtureIdentity { + pub label: &'static str, + pub username: &'static str, + pub email: &'static str, + pub secret_key_hex: &'static str, + pub public_key_hex: &'static str, + pub nsec: &'static str, + pub npub: &'static str, +} + +pub const APPROVED_FIXTURE_NAMESPACE: &str = "radroots-approved-fixture-v1"; + +pub const FIXTURE_ALICE_LABEL: &str = "fixture_alice"; +pub const FIXTURE_ALICE_USERNAME: &str = "fixture_alice"; +pub const FIXTURE_ALICE_EMAIL: &str = "fixture_alice@fixtures.test"; +pub const FIXTURE_ALICE_SECRET_KEY_HEX: &str = + "10c5304d6c9ae3a1a16f7860f1cc8f5e3a76225a2663b3a989a0d775919b7df5"; +pub const FIXTURE_ALICE_PUBLIC_KEY_HEX: &str = + "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df"; +pub const FIXTURE_ALICE_NSEC: &str = + "nsec1zrznqntvnt36rgt00ps0rny0tca8vgj6ye3m82vf5rthtyvm0h6syu7drz"; +pub const FIXTURE_ALICE_NPUB: &str = + "npub1tp2ez55a5zatxxemrv0eses3ea05xhw2snuh3jy7azjqejn3q00s3vy5a9"; +pub const FIXTURE_ALICE: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_ALICE_LABEL, + username: FIXTURE_ALICE_USERNAME, + email: FIXTURE_ALICE_EMAIL, + secret_key_hex: FIXTURE_ALICE_SECRET_KEY_HEX, + public_key_hex: FIXTURE_ALICE_PUBLIC_KEY_HEX, + nsec: FIXTURE_ALICE_NSEC, + npub: FIXTURE_ALICE_NPUB, +}; + +pub const FIXTURE_BOB_LABEL: &str = "fixture_bob"; +pub const FIXTURE_BOB_USERNAME: &str = "fixture_bob"; +pub const FIXTURE_BOB_EMAIL: &str = "fixture_bob@fixtures.test"; +pub const FIXTURE_BOB_SECRET_KEY_HEX: &str = + "59392e9068f66431b12f70218fb61281cb6b433d7f27c55d61f1a63fe1a96ff8"; +pub const FIXTURE_BOB_PUBLIC_KEY_HEX: &str = + "e0266e3cfb0d2886f91c73f5f868f3b98273713e5fcd97c081663f5518a4b3af"; +pub const FIXTURE_BOB_NSEC: &str = + "nsec1tyujayrg7ejrrvf0wqscldsjs89kksea0unu2htp7xnrlcdfdluqrjya9h"; +pub const FIXTURE_BOB_NPUB: &str = + "npub1uqnxu08mp55gd7guw06ls68nhxp8xuf7tlxe0sypvcl42x9ykwhsd55k2g"; +pub const FIXTURE_BOB: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_BOB_LABEL, + username: FIXTURE_BOB_USERNAME, + email: FIXTURE_BOB_EMAIL, + secret_key_hex: FIXTURE_BOB_SECRET_KEY_HEX, + public_key_hex: FIXTURE_BOB_PUBLIC_KEY_HEX, + nsec: FIXTURE_BOB_NSEC, + npub: FIXTURE_BOB_NPUB, +}; + +pub const FIXTURE_CAROL_LABEL: &str = "fixture_carol"; +pub const FIXTURE_CAROL_USERNAME: &str = "fixture_carol"; +pub const FIXTURE_CAROL_EMAIL: &str = "fixture_carol@fixtures.test"; +pub const FIXTURE_CAROL_SECRET_KEY_HEX: &str = + "4d6c20fdd86857de77ff5cfa5c545751ba2efd126e0b6642dae9764d782d6509"; +pub const FIXTURE_CAROL_PUBLIC_KEY_HEX: &str = + "1952b8c6943898bceffcff1b7699c4a775a4d13b4a9ba0096ba26ef04492bb1c"; +pub const FIXTURE_CAROL_NSEC: &str = + "nsec1f4kzplwcdptaualltna9c4zh2xazalgjdc9kvsk6a9my67pdv5ys2pqkaj"; +pub const FIXTURE_CAROL_NPUB: &str = + "npub1r9ft33558zvtemluludhdxwy5a66f5fmf2d6qztt5fh0q3yjhvwqgzmkl6"; +pub const FIXTURE_CAROL: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_CAROL_LABEL, + username: FIXTURE_CAROL_USERNAME, + email: FIXTURE_CAROL_EMAIL, + secret_key_hex: FIXTURE_CAROL_SECRET_KEY_HEX, + public_key_hex: FIXTURE_CAROL_PUBLIC_KEY_HEX, + nsec: FIXTURE_CAROL_NSEC, + npub: FIXTURE_CAROL_NPUB, +}; + +pub const FIXTURE_DIEGO_LABEL: &str = "fixture_diego"; +pub const FIXTURE_DIEGO_USERNAME: &str = "fixture_diego"; +pub const FIXTURE_DIEGO_EMAIL: &str = "fixture_diego@fixtures.test"; +pub const FIXTURE_DIEGO_SECRET_KEY_HEX: &str = + "9de56c1fdfce9ab00af85b3d7003c1d15cffb84cdf303c3a83c1a3fb1a2d0db0"; +pub const FIXTURE_DIEGO_PUBLIC_KEY_HEX: &str = + "5d3eab6e78eb7e467a9e196a63456c9fafb93fb88b7052b83229870889923aa4"; +pub const FIXTURE_DIEGO_NSEC: &str = + "nsec1nhjkc87le6dtqzhctv7hqq7p69w0lwzvmucrcw5rcx3lkx3dpkcqkrmgp5"; +pub const FIXTURE_DIEGO_NPUB: &str = + "npub1t5l2kmncadlyv757r94xx3tvn7hmj0ac3dc99wpj9xrs3zvj82jqwwcglm"; +pub const FIXTURE_DIEGO: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_DIEGO_LABEL, + username: FIXTURE_DIEGO_USERNAME, + email: FIXTURE_DIEGO_EMAIL, + secret_key_hex: FIXTURE_DIEGO_SECRET_KEY_HEX, + public_key_hex: FIXTURE_DIEGO_PUBLIC_KEY_HEX, + nsec: FIXTURE_DIEGO_NSEC, + npub: FIXTURE_DIEGO_NPUB, +}; + +pub const RELAY_PRIMARY_WSS: &str = "wss://relay.example.com"; +pub const RELAY_SECONDARY_WSS: &str = "wss://relay-2.example.com"; +pub const RELAY_TERTIARY_WSS: &str = "wss://relay-3.example.com"; + +pub const APP_PRIMARY_HTTPS: &str = "https://app.example.com"; +pub const API_PRIMARY_HTTPS: &str = "https://api.example.com"; +pub const CDN_PRIMARY_HTTPS: &str = "https://cdn.example.com"; diff --git a/crates/nostr_connect/src/lib.rs b/crates/nostr_connect/src/lib.rs @@ -22,8 +22,8 @@ pub use uri::{BunkerUri, ClientUri}; /// Private migration surface for separate first-party repositories. /// /// This module is excluded from the reviewed public API baseline, the package -/// remains `publish = false`, consumers cut over in Steps 271, 288, and 293, -/// and Step 313 removes the shim in full. +/// is enabled only for package-realistic validation, consumers cut over in +/// Steps 271, 288, and 293, and Step 313 removes the shim in full. #[doc(hidden)] pub mod prelude { pub use crate::client::{ diff --git a/crates/nostr_connect/tests/package_boundary.rs b/crates/nostr_connect/tests/package_boundary.rs @@ -31,7 +31,7 @@ fn manifest_has_final_identity_feature_vocabulary_and_radroots_dependencies() { for required in [ "name = \"radroots_nostr_connect\"", "version = \"0.1.0-alpha\"", - "publish = false", + "publish = [\"crates-io\"]", "[lib]\nname = \"radroots_nostr_connect\"", "default = [\"serde\"]", "radroots_event = { workspace = true, default-features = false }", @@ -234,7 +234,7 @@ fn client_root_and_transport_use_package_owned_state_machine_types() { #[test] fn separate_repository_compatibility_is_hidden_unpublished_and_scheduled() { - assert!(MANIFEST.contains("publish = false")); + assert!(MANIFEST.contains("publish = [\"crates-io\"]")); for item in [ "pub type RadrootsNostrConnectClientTransportFuture", "pub struct RadrootsNostrConnectClientTarget", diff --git a/crates/protocol/tests/package_boundary.rs b/crates/protocol/tests/package_boundary.rs @@ -20,7 +20,7 @@ const VERSIONED_SOURCES: &[&str] = &[ fn manifest_has_final_identity_features_and_no_radroots_dependencies() { assert!(MANIFEST.contains("name = \"radroots_protocol\"")); assert!(MANIFEST.contains("version = \"0.1.0-alpha\"")); - assert!(MANIFEST.contains("publish = false")); + assert!(MANIFEST.contains("publish = [\"crates-io\"]")); assert!(MANIFEST.contains("[lib]\nname = \"radroots_protocol\"")); assert!(MANIFEST.contains("default = [\"std\", \"serde\"]")); assert_eq!( diff --git a/crates/secrets/tests/package_boundary.rs b/crates/secrets/tests/package_boundary.rs @@ -12,7 +12,7 @@ fn manifest_has_final_identity_features_and_no_radroots_dependencies() { for required in [ "name = \"radroots_secrets\"", "version = \"0.1.0-alpha\"", - "publish = false", + "publish = [\"crates-io\"]", "documentation = \"https://docs.rs/radroots_secrets\"", "[lib]\nname = \"radroots_secrets\"", "default = [\"std\", \"serde\"]", diff --git a/crates/signing/tests/package_boundary.rs b/crates/signing/tests/package_boundary.rs @@ -17,7 +17,7 @@ fn manifest_has_final_identity_features_and_dependencies() { for required in [ "name = \"radroots_signing\"", "version = \"0.1.0-alpha\"", - "publish = false", + "publish = [\"crates-io\"]", "default = [\"std\", \"serde\"]", "radroots_event = { workspace = true, default-features = false }", "radroots_identity = { workspace = true, default-features = false }", diff --git a/crates/storage_sqlite/tests/package_boundary.rs b/crates/storage_sqlite/tests/package_boundary.rs @@ -11,7 +11,7 @@ fn sqlite_storage_declares_the_final_backend_boundaries() { for required in [ "name = \"radroots_storage_sqlite\"", "version = \"0.1.0-alpha\"", - "publish = false", + "publish = [\"crates-io\"]", "[lib]\nname = \"radroots_storage_sqlite\"", ] { assert!( diff --git a/crates/sync/tests/package_boundary.rs b/crates/sync/tests/package_boundary.rs @@ -18,7 +18,7 @@ fn sync_depends_only_on_final_orchestration_boundaries() { for required in [ "name = \"radroots_sync\"", "version = \"0.1.0-alpha\"", - "publish = false", + "publish = [\"crates-io\"]", "[lib]\nname = \"radroots_sync\"", "default = [\"serde\"]", ] { diff --git a/crates/trade/Cargo.toml b/crates/trade/Cargo.toml @@ -73,7 +73,6 @@ sha2 = { workspace = true, default-features = false, optional = true } [dev-dependencies] nostr = { workspace = true, features = ["std"] } -radroots_test_fixtures = { workspace = true } [lints] workspace = true diff --git a/crates/trade/src/lib.rs b/crates/trade/src/lib.rs @@ -6,6 +6,9 @@ #[cfg(not(feature = "std"))] extern crate alloc; +#[cfg(test)] +mod test_fixtures; + // TEMPORARY COMPATIBILITY QUARANTINE (publish = false): binding generation, // operational-listing consumers, and SP1 validation receipts migrate in // Steps 238 and 261-262. Every declaration below is removed in Step 313. diff --git a/crates/trade/src/operational_listing/draft.rs b/crates/trade/src/operational_listing/draft.rs @@ -176,6 +176,7 @@ pub fn canonicalize_operational_listing_edit( #[cfg(test)] mod tests { + use crate::test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX}; use radroots_core::{Currency, Decimal, Money, Quantity, QuantityPrice, Unit}; use radroots_event::{ envelope::kind::KIND_CLASSIFIED_LISTING, @@ -189,7 +190,6 @@ mod tests { trade::validation::OperationalListingValidationError, }; use radroots_identity::PublicKey; - use radroots_test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX}; use super::{ RadrootsOperationalListingCanonicalEdit, RadrootsOperationalListingEditDocumentV1, diff --git a/crates/trade/src/operational_listing/mutation.rs b/crates/trade/src/operational_listing/mutation.rs @@ -169,6 +169,7 @@ pub fn build_operational_listing_mutation_draft( #[cfg(test)] mod tests { + use crate::test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_ALICE_SECRET_KEY_HEX}; use nostr::{EventBuilder, JsonUtil, Keys, Kind, Tag, Timestamp}; use radroots_core::{Currency, Decimal, Money, Quantity, QuantityPrice, Unit}; use radroots_event::{ @@ -187,7 +188,6 @@ mod tests { }; use radroots_event_codec::verification::verify_nip01_event; use radroots_identity::PublicKey; - use radroots_test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_ALICE_SECRET_KEY_HEX}; use crate::operational_listing::draft::RadrootsOperationalListingCanonicalEdit; use crate::operational_listing::validation::validate_operational_listing_event; diff --git a/crates/trade/src/operational_listing/validation.rs b/crates/trade/src/operational_listing/validation.rs @@ -233,6 +233,10 @@ mod tests { OperationalListingValidationError, validate_operational_listing_event, validate_operational_listing_model, }; + use crate::test_fixtures::{ + FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_ALICE_SECRET_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX, + FIXTURE_BOB_SECRET_KEY_HEX, + }; use nostr::{EventBuilder, JsonUtil, Keys, Kind, Tag, Timestamp}; use radroots_core::{Currency, Decimal, Money, Quantity, QuantityPrice, Unit}; use radroots_event::{ @@ -250,10 +254,6 @@ mod tests { }; use radroots_event_codec::verification::{RadrootsSignatureVerifiedEvent, verify_nip01_event}; use radroots_identity::PublicKey; - use radroots_test_fixtures::{ - FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_ALICE_SECRET_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX, - FIXTURE_BOB_SECRET_KEY_HEX, - }; const SELLER: &str = FIXTURE_ALICE_PUBLIC_KEY_HEX; const OTHER_SELLER: &str = FIXTURE_BOB_PUBLIC_KEY_HEX; diff --git a/crates/trade/src/reducer_impl.rs b/crates/trade/src/reducer_impl.rs @@ -1382,6 +1382,7 @@ fn projection_digest(_projection: &RadrootsTradeProjectionV1) -> Result<String, #[cfg_attr(coverage_nightly, coverage(off))] mod tests { use super::*; + use crate::test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX}; use radroots_event::{ id::{ClassifiedListingAddress, InventoryBinId}, trade::{ @@ -1393,7 +1394,6 @@ mod tests { canonical_trade_mutation_content, }, }; - use radroots_test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX}; const CANONICAL_REDUCER_VECTORS: &str = include_str!("../../../contracts/conformance/vectors/trade/reduce_records.v1.json"); diff --git a/crates/trade/src/test_fixtures.rs b/crates/trade/src/test_fixtures.rs @@ -0,0 +1,124 @@ +#![forbid(unsafe_code)] +#![allow(dead_code)] + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ApprovedFixtureIdentity { + pub label: &'static str, + pub username: &'static str, + pub email: &'static str, + pub secret_key_hex: &'static str, + pub public_key_hex: &'static str, + pub nsec: &'static str, + pub npub: &'static str, +} + +pub const APPROVED_FIXTURE_NAMESPACE: &str = "radroots-approved-fixture-v1"; + +pub const FIXTURE_ALICE_LABEL: &str = "fixture_alice"; +pub const FIXTURE_ALICE_USERNAME: &str = "fixture_alice"; +pub const FIXTURE_ALICE_EMAIL: &str = "fixture_alice@fixtures.test"; +pub const FIXTURE_ALICE_SECRET_KEY_HEX: &str = + "10c5304d6c9ae3a1a16f7860f1cc8f5e3a76225a2663b3a989a0d775919b7df5"; +pub const FIXTURE_ALICE_PUBLIC_KEY_HEX: &str = + "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df"; +pub const FIXTURE_ALICE_NSEC: &str = + "nsec1zrznqntvnt36rgt00ps0rny0tca8vgj6ye3m82vf5rthtyvm0h6syu7drz"; +pub const FIXTURE_ALICE_NPUB: &str = + "npub1tp2ez55a5zatxxemrv0eses3ea05xhw2snuh3jy7azjqejn3q00s3vy5a9"; +pub const FIXTURE_ALICE: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_ALICE_LABEL, + username: FIXTURE_ALICE_USERNAME, + email: FIXTURE_ALICE_EMAIL, + secret_key_hex: FIXTURE_ALICE_SECRET_KEY_HEX, + public_key_hex: FIXTURE_ALICE_PUBLIC_KEY_HEX, + nsec: FIXTURE_ALICE_NSEC, + npub: FIXTURE_ALICE_NPUB, +}; + +pub const FIXTURE_BOB_LABEL: &str = "fixture_bob"; +pub const FIXTURE_BOB_USERNAME: &str = "fixture_bob"; +pub const FIXTURE_BOB_EMAIL: &str = "fixture_bob@fixtures.test"; +pub const FIXTURE_BOB_SECRET_KEY_HEX: &str = + "59392e9068f66431b12f70218fb61281cb6b433d7f27c55d61f1a63fe1a96ff8"; +pub const FIXTURE_BOB_PUBLIC_KEY_HEX: &str = + "e0266e3cfb0d2886f91c73f5f868f3b98273713e5fcd97c081663f5518a4b3af"; +pub const FIXTURE_BOB_NSEC: &str = + "nsec1tyujayrg7ejrrvf0wqscldsjs89kksea0unu2htp7xnrlcdfdluqrjya9h"; +pub const FIXTURE_BOB_NPUB: &str = + "npub1uqnxu08mp55gd7guw06ls68nhxp8xuf7tlxe0sypvcl42x9ykwhsd55k2g"; +pub const FIXTURE_BOB: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_BOB_LABEL, + username: FIXTURE_BOB_USERNAME, + email: FIXTURE_BOB_EMAIL, + secret_key_hex: FIXTURE_BOB_SECRET_KEY_HEX, + public_key_hex: FIXTURE_BOB_PUBLIC_KEY_HEX, + nsec: FIXTURE_BOB_NSEC, + npub: FIXTURE_BOB_NPUB, +}; + +pub const FIXTURE_CAROL_LABEL: &str = "fixture_carol"; +pub const FIXTURE_CAROL_USERNAME: &str = "fixture_carol"; +pub const FIXTURE_CAROL_EMAIL: &str = "fixture_carol@fixtures.test"; +pub const FIXTURE_CAROL_SECRET_KEY_HEX: &str = + "4d6c20fdd86857de77ff5cfa5c545751ba2efd126e0b6642dae9764d782d6509"; +pub const FIXTURE_CAROL_PUBLIC_KEY_HEX: &str = + "1952b8c6943898bceffcff1b7699c4a775a4d13b4a9ba0096ba26ef04492bb1c"; +pub const FIXTURE_CAROL_NSEC: &str = + "nsec1f4kzplwcdptaualltna9c4zh2xazalgjdc9kvsk6a9my67pdv5ys2pqkaj"; +pub const FIXTURE_CAROL_NPUB: &str = + "npub1r9ft33558zvtemluludhdxwy5a66f5fmf2d6qztt5fh0q3yjhvwqgzmkl6"; +pub const FIXTURE_CAROL: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_CAROL_LABEL, + username: FIXTURE_CAROL_USERNAME, + email: FIXTURE_CAROL_EMAIL, + secret_key_hex: FIXTURE_CAROL_SECRET_KEY_HEX, + public_key_hex: FIXTURE_CAROL_PUBLIC_KEY_HEX, + nsec: FIXTURE_CAROL_NSEC, + npub: FIXTURE_CAROL_NPUB, +}; + +pub const FIXTURE_DIEGO_LABEL: &str = "fixture_diego"; +pub const FIXTURE_DIEGO_USERNAME: &str = "fixture_diego"; +pub const FIXTURE_DIEGO_EMAIL: &str = "fixture_diego@fixtures.test"; +pub const FIXTURE_DIEGO_SECRET_KEY_HEX: &str = + "9de56c1fdfce9ab00af85b3d7003c1d15cffb84cdf303c3a83c1a3fb1a2d0db0"; +pub const FIXTURE_DIEGO_PUBLIC_KEY_HEX: &str = + "5d3eab6e78eb7e467a9e196a63456c9fafb93fb88b7052b83229870889923aa4"; +pub const FIXTURE_DIEGO_NSEC: &str = + "nsec1nhjkc87le6dtqzhctv7hqq7p69w0lwzvmucrcw5rcx3lkx3dpkcqkrmgp5"; +pub const FIXTURE_DIEGO_NPUB: &str = + "npub1t5l2kmncadlyv757r94xx3tvn7hmj0ac3dc99wpj9xrs3zvj82jqwwcglm"; +pub const FIXTURE_DIEGO: ApprovedFixtureIdentity = ApprovedFixtureIdentity { + label: FIXTURE_DIEGO_LABEL, + username: FIXTURE_DIEGO_USERNAME, + email: FIXTURE_DIEGO_EMAIL, + secret_key_hex: FIXTURE_DIEGO_SECRET_KEY_HEX, + public_key_hex: FIXTURE_DIEGO_PUBLIC_KEY_HEX, + nsec: FIXTURE_DIEGO_NSEC, + npub: FIXTURE_DIEGO_NPUB, +}; + +pub fn fixture_public_key_hex(character: char) -> String { + let canonical = match character.to_ascii_lowercase() { + 'a' => FIXTURE_ALICE_PUBLIC_KEY_HEX, + 'b' => FIXTURE_BOB_PUBLIC_KEY_HEX, + 'c' => FIXTURE_CAROL_PUBLIC_KEY_HEX, + 'd' => FIXTURE_DIEGO_PUBLIC_KEY_HEX, + 'e' => "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + 'f' => "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9", + _ => panic!("unsupported fixture public-key label: {character}"), + }; + if character.is_ascii_uppercase() { + canonical.to_ascii_uppercase() + } else { + canonical.to_owned() + } +} + +pub const RELAY_PRIMARY_WSS: &str = "wss://relay.example.com"; +pub const RELAY_SECONDARY_WSS: &str = "wss://relay-2.example.com"; +pub const RELAY_TERTIARY_WSS: &str = "wss://relay-3.example.com"; + +pub const APP_PRIMARY_HTTPS: &str = "https://app.example.com"; +pub const API_PRIMARY_HTTPS: &str = "https://api.example.com"; +pub const CDN_PRIMARY_HTTPS: &str = "https://cdn.example.com"; diff --git a/crates/trade/src/validation_receipt.rs b/crates/trade/src/validation_receipt.rs @@ -1174,6 +1174,9 @@ mod tests { validator_set_content_from_str, validator_set_event_build, validator_set_from_event, verify_validation_receipt_event, verify_validator_set_event, }; + use crate::test_fixtures::{ + FIXTURE_BOB_PUBLIC_KEY_HEX, FIXTURE_CAROL_PUBLIC_KEY_HEX, FIXTURE_DIEGO_PUBLIC_KEY_HEX, + }; use radroots_event::{ envelope::EventEnvelope, envelope::EventEnvelopeParts, @@ -1182,9 +1185,6 @@ mod tests { tag::name::TAG_D, }; use radroots_identity::PublicKey; - use radroots_test_fixtures::{ - FIXTURE_BOB_PUBLIC_KEY_HEX, FIXTURE_CAROL_PUBLIC_KEY_HEX, FIXTURE_DIEGO_PUBLIC_KEY_HEX, - }; fn hash32(c: char) -> String { format!("0x{}", c.to_string().repeat(64)) diff --git a/crates/trade/src/workflow.rs b/crates/trade/src/workflow.rs @@ -259,6 +259,7 @@ fn private_terms_plan(body: &TradeMutationBodyV1) -> Result<Option<PrivateTermsP #[cfg(test)] mod tests { use super::*; + use crate::test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX}; use radroots_event::{ id::{ClassifiedListingAddress, DTag, EventId, InventoryBinId}, trade::{ @@ -272,7 +273,6 @@ mod tests { }, }; use radroots_identity::PublicKey; - use radroots_test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX}; #[cfg(feature = "json")] const CANONICAL_WORKFLOW_VECTORS: &str = diff --git a/crates/trade/tests/package_boundary.rs b/crates/trade/tests/package_boundary.rs @@ -26,7 +26,7 @@ const COMPATIBILITY: &str = include_str!("../COMPATIBILITY.md"); fn manifest_has_final_identity_and_required_radroots_dependencies() { assert!(MANIFEST.contains("name = \"radroots_trade\"")); assert!(MANIFEST.contains("version = \"0.1.0-alpha\"")); - assert!(MANIFEST.contains("publish = false")); + assert!(MANIFEST.contains("publish = [\"crates-io\"]")); assert!(MANIFEST.contains("[lib]\nname = \"radroots_trade\"")); let dependencies = table_keys(MANIFEST, "[dependencies]"); diff --git a/crates/transport/tests/package_boundary.rs b/crates/transport/tests/package_boundary.rs @@ -25,7 +25,7 @@ fn manifest_has_final_identity_features_and_required_radroots_dependencies() { for required in [ "name = \"radroots_transport\"", "version = \"0.1.0-alpha\"", - "publish = false", + "publish = [\"crates-io\"]", "[lib]\nname = \"radroots_transport\"", "default = [\"std\", \"serde\"]", "radroots_event = { workspace = true, default-features = false }", diff --git a/crates/transport_nostr/tests/conformance.rs b/crates/transport_nostr/tests/conformance.rs @@ -34,7 +34,7 @@ fn public_types_and_split_spis_satisfy_the_adapter_contract() { #[test] fn root_and_manifest_expose_no_traits_features_or_ambient_runtime() { assert!(!MANIFEST.contains("[features]")); - assert!(MANIFEST.contains("publish = false")); + assert!(MANIFEST.contains("publish = [\"crates-io\"]")); for forbidden in [ "pub trait ", "pub mod ", diff --git a/crates/transport_nostr/tests/package_boundary.rs b/crates/transport_nostr/tests/package_boundary.rs @@ -14,7 +14,7 @@ fn manifest_and_root_match_the_governed_transport_boundary() { for required in [ "name = \"radroots_transport_nostr\"", "version = \"0.1.0-alpha\"", - "publish = false", + "publish = [\"crates-io\"]", "[lib]\nname = \"radroots_transport_nostr\"", ] { assert!( diff --git a/tools/xtask/src/architecture.rs b/tools/xtask/src/architecture.rs @@ -611,6 +611,13 @@ fn validate_public_dependency_table( let dependency_name = declared_package .or(path_package.as_deref()) .unwrap_or(dependency_key.as_str()); + let first_party = + dependency_key.starts_with("radroots_") || dependency_name.starts_with("radroots_"); + if first_party && !policy.public_packages.contains(dependency_name) { + return Err(format!( + "public package {owner} {section}.{dependency_key} must not depend on private first-party package {dependency_name}" + )); + } if !policy.public_packages.contains(dependency_name) { continue; }