@feltdb/core 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-state.d.ts +20 -0
- package/dist/agent-state.d.ts.map +1 -1
- package/dist/agent-state.js +36 -0
- package/dist/build-identity.json +12 -4
- package/dist/cli/commands.js +187 -4
- package/dist/cli/publish-engine.js +116 -16
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +3 -0
- package/dist/create/build-identity.json +12 -4
- package/dist/create/managed-account.js +93 -35
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +6 -509
- package/dist/create/server-source/Cargo.toml +2 -13
- package/dist/create/server-source/crates/feltdb-client/Cargo.toml +1 -1
- package/dist/create/server-source/crates/feltdb-server/Cargo.toml +1 -1
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +255 -12
- package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +1 -1
- package/dist/db.d.ts +11 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +15 -2
- package/dist/error-codes.d.ts +5 -0
- package/dist/error-codes.d.ts.map +1 -1
- package/dist/error-codes.js +9 -1
- package/dist/http-db.d.ts +16 -0
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +36 -6
- package/dist/managed-recovery.d.ts +6 -0
- package/dist/managed-recovery.d.ts.map +1 -1
- package/dist/managed-recovery.js +9 -0
- package/dist/studio-app/assets/{feltdb_wasm-BjM1ylL3.js → feltdb_wasm-DtybUY0q.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-DsgIUwaB.wasm +0 -0
- package/dist/studio-app/assets/index-C4OffsZl.js +37 -0
- package/dist/studio-app/index.html +1 -1
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/wasm/package.json +1 -1
- package/dist/workload.d.ts +55 -3
- package/dist/workload.d.ts.map +1 -1
- package/dist/workload.js +23 -0
- package/dist/workspace/workspace-connection.d.ts.map +1 -1
- package/dist/workspace/workspace-connection.js +14 -3
- package/package.json +1 -1
- package/dist/create/server-source/crates/feltdb/Cargo.lock +0 -175
- package/dist/create/server-source/crates/feltdb/Cargo.toml +0 -102
- package/dist/create/server-source/crates/feltdb/LICENSE +0 -21
- package/dist/create/server-source/crates/feltdb/README.md +0 -75
- package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +0 -370
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +0 -589
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +0 -247
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +0 -440
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +0 -275
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +0 -381
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +0 -467
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +0 -489
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +0 -281
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +0 -606
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +0 -531
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +0 -462
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +0 -505
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +0 -484
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +0 -758
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +0 -421
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +0 -329
- package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +0 -1705
- package/dist/create/server-source/crates/feltdb/src/acquisition.rs +0 -286
- package/dist/create/server-source/crates/feltdb/src/admission.rs +0 -185
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +0 -507
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +0 -539
- package/dist/create/server-source/crates/feltdb/src/agent_state.rs +0 -320
- package/dist/create/server-source/crates/feltdb/src/agent_transition_contract.rs +0 -1545
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +0 -521
- package/dist/create/server-source/crates/feltdb/src/application.rs +0 -2674
- package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +0 -1070
- package/dist/create/server-source/crates/feltdb/src/authority.rs +0 -479
- package/dist/create/server-source/crates/feltdb/src/authority_failover.rs +0 -847
- package/dist/create/server-source/crates/feltdb/src/authorization.rs +0 -1030
- package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +0 -1122
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority-client.rs +0 -32
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority.rs +0 -19
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +0 -1474
- package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +0 -8
- package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +0 -418
- package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +0 -482
- package/dist/create/server-source/crates/feltdb/src/capability.rs +0 -903
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +0 -263
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -77
- package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +0 -496
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +0 -2574
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +0 -212
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +0 -422
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +0 -445
- package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +0 -465
- package/dist/create/server-source/crates/feltdb/src/convergence.rs +0 -634
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +0 -427
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +0 -376
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +0 -365
- package/dist/create/server-source/crates/feltdb/src/cron.rs +0 -294
- package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +0 -483
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +0 -477
- package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +0 -1003
- package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +0 -1334
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +0 -368
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +0 -231
- package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +0 -482
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +0 -203
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +0 -317
- package/dist/create/server-source/crates/feltdb/src/equality_index.rs +0 -595
- package/dist/create/server-source/crates/feltdb/src/execution.rs +0 -426
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +0 -214
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +0 -776
- package/dist/create/server-source/crates/feltdb/src/lib.rs +0 -7365
- package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +0 -234
- package/dist/create/server-source/crates/feltdb/src/materialization.rs +0 -184
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -266
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +0 -310
- package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +0 -736
- package/dist/create/server-source/crates/feltdb/src/observability.rs +0 -379
- package/dist/create/server-source/crates/feltdb/src/operation.rs +0 -290
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +0 -439
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +0 -349
- package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +0 -332
- package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +0 -718
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +0 -487
- package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +0 -166
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +0 -289
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +0 -566
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +0 -3391
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +0 -203
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +0 -261
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +0 -502
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +0 -216
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +0 -303
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +0 -323
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +0 -531
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +0 -406
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +0 -436
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +0 -387
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +0 -378
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +0 -359
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +0 -372
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +0 -404
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +0 -376
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +0 -461
- package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +0 -2125
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +0 -418
- package/dist/create/server-source/crates/feltdb/src/provenance.rs +0 -207
- package/dist/create/server-source/crates/feltdb/src/query_execution_diagnostics.rs +0 -126
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +0 -218
- package/dist/create/server-source/crates/feltdb/src/read_basis.rs +0 -257
- package/dist/create/server-source/crates/feltdb/src/references.rs +0 -404
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +0 -401
- package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +0 -501
- package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +0 -680
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +0 -163
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +0 -133
- package/dist/create/server-source/crates/feltdb/src/routing.rs +0 -409
- package/dist/create/server-source/crates/feltdb/src/semantic_decision.rs +0 -2029
- package/dist/create/server-source/crates/feltdb/src/semantic_query.rs +0 -599
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +0 -549
- package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +0 -516
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +0 -6003
- package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +0 -222
- package/dist/create/server-source/crates/feltdb/src/state_facade.rs +0 -320
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +0 -291
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +0 -4158
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +0 -379
- package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +0 -673
- package/dist/create/server-source/crates/feltdb/src/storage.rs +0 -273
- package/dist/create/server-source/crates/feltdb/src/submission.rs +0 -471
- package/dist/create/server-source/crates/feltdb/src/sync.rs +0 -495
- package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +0 -822
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +0 -433
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +0 -462
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +0 -965
- package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +0 -1120
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +0 -643
- package/dist/create/server-source/crates/feltdb/src/trigger.rs +0 -390
- package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +0 -749
- package/dist/create/server-source/crates/feltdb/src/versioned_object_substrate.rs +0 -708
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +0 -1269
- package/dist/create/server-source/crates/feltdb/src/workflow.rs +0 -713
- package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +0 -510
- package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +0 -354
- package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +0 -594
- package/dist/create/server-source/crates/feltdb/src/workload.rs +0 -4222
- package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +0 -456
- package/dist/create/server-source/crates/feltdb/tests/agent_state_native.rs +0 -1188
- package/dist/create/server-source/crates/feltdb/tests/agent_transition_contract_conformance.rs +0 -2884
- package/dist/create/server-source/crates/feltdb/tests/authority_process.rs +0 -297
- package/dist/create/server-source/crates/feltdb/tests/authority_transaction_identity.rs +0 -534
- package/dist/create/server-source/crates/feltdb/tests/bounded_read_contract.rs +0 -132
- package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +0 -299
- package/dist/create/server-source/crates/feltdb/tests/common/mod.rs +0 -96
- package/dist/create/server-source/crates/feltdb/tests/compaction_stall_contract.rs +0 -272
- package/dist/create/server-source/crates/feltdb/tests/crash_durability_contract.rs +0 -467
- package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +0 -309
- package/dist/create/server-source/crates/feltdb/tests/durable_backup_contract.rs +0 -445
- package/dist/create/server-source/crates/feltdb/tests/durable_corruption_contract.rs +0 -518
- package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +0 -392
- package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +0 -850
- package/dist/create/server-source/crates/feltdb/tests/fixtures/agent_transition_corpus.json +0 -23340
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +0 -1916
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +0 -1878
- package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +0 -1862
- package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_evidence.rs +0 -123
- package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_substrate_completion.rs +0 -299
- package/dist/create/server-source/crates/feltdb/tests/local_process_ownership.rs +0 -69
- package/dist/create/server-source/crates/feltdb/tests/managed_incident_regression.rs +0 -191
- package/dist/create/server-source/crates/feltdb/tests/operational_health_contract.rs +0 -278
- package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +0 -234
- package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +0 -947
- package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +0 -404
- package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +0 -916
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +0 -1045
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +0 -347
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +0 -361
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +0 -330
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +0 -93
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +0 -622
- package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +0 -517
- package/dist/create/server-source/crates/feltdb/tests/production_certification.rs +0 -1153
- package/dist/create/server-source/crates/feltdb/tests/production_contract.rs +0 -771
- package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +0 -1383
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +0 -868
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +0 -221
- package/dist/create/server-source/crates/feltdb/tests/replicated_history_contract.rs +0 -417
- package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +0 -338
- package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +0 -1039
- package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +0 -739
- package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +0 -452
- package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +0 -81
- package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +0 -438
- package/dist/create/server-source/crates/feltdb/tests/semantic_decision.rs +0 -1165
- package/dist/create/server-source/crates/feltdb/tests/semantic_query.rs +0 -767
- package/dist/create/server-source/crates/feltdb/tests/semantic_query_contract.rs +0 -363
- package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +0 -1799
- package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +0 -1316
- package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +0 -358
- package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +0 -365
- package/dist/create/server-source/crates/feltdb/tests/state_ref_authority.rs +0 -918
- package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +0 -299
- package/dist/create/server-source/crates/feltdb/tests/storage_independent_versioned_object_substrate.rs +0 -328
- package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +0 -255
- package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +0 -249
- package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +0 -994
- package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +0 -484
- package/dist/studio-app/assets/feltdb_wasm_bg-TseJn2Ax.wasm +0 -0
- package/dist/studio-app/assets/index-1wd0uKua.js +0 -30
package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs
DELETED
|
@@ -1,452 +0,0 @@
|
|
|
1
|
-
//! Evidence for the revision retention and identity boundary audit
|
|
2
|
-
//! (`docs/audits/REVISION-RETENTION-BOUNDARY-AUDIT.md`).
|
|
3
|
-
//!
|
|
4
|
-
//! Adds **no capability and no product surface**. It tests one question:
|
|
5
|
-
//!
|
|
6
|
-
//! > The [three-way input boundary audit](../../../docs/audits/THREE-WAY-INPUT-BOUNDARY-AUDIT.md)
|
|
7
|
-
//! > proved that minting a `StateRevision` at the authoritative mutation is the
|
|
8
|
-
//! > only candidate that can supply `base`. What does doing so *cost*, and what
|
|
9
|
-
//! > bounds it?
|
|
10
|
-
//!
|
|
11
|
-
//! Two results decide it. The cost is measurable and linear, with a fixed
|
|
12
|
-
//! per-revision overhead of roughly half a kilobyte that nothing collects. And
|
|
13
|
-
//! content-only identity does not survive per-mutation minting: a resource that
|
|
14
|
-
//! returns to a previous value silently rewrites the ancestry of the revision it
|
|
15
|
-
//! returns to, producing a cycle in the durable parent chain.
|
|
16
|
-
//!
|
|
17
|
-
//! # Superseded in part
|
|
18
|
-
//!
|
|
19
|
-
//! The identity findings here caused the resource-scoped model to be adopted,
|
|
20
|
-
//! and are **fixed**: the tests that recorded them now record the fix instead,
|
|
21
|
-
//! and say so individually. Two consequences of that change are recorded here
|
|
22
|
-
//! rather than argued:
|
|
23
|
-
//!
|
|
24
|
-
//! - **Content addressing no longer bounds anything.** Identity includes the
|
|
25
|
-
//! resource and a sequence, so repeated state does not collapse. The bound
|
|
26
|
-
//! this audit found is gone, replaced by an explicit retention policy.
|
|
27
|
-
//! - **The cost constants changed.** A revision now carries a resource, a
|
|
28
|
-
//! second identity and a sequence. The shape is still linear; the constants
|
|
29
|
-
//! are not the ones measured here.
|
|
30
|
-
//!
|
|
31
|
-
//! Re-measuring the economics against the adopted model is deliberately *not*
|
|
32
|
-
//! done here — it is the next step, and doing it inside the implementation
|
|
33
|
-
//! would mix a measurement with the change being measured.
|
|
34
|
-
|
|
35
|
-
use feltdb::state_model::{ParentLookup, RetentionPolicy, StateStore};
|
|
36
|
-
use feltdb::{FeltDb, JsonCasResult};
|
|
37
|
-
use serde_json::json;
|
|
38
|
-
use std::sync::Arc;
|
|
39
|
-
use tempfile::TempDir;
|
|
40
|
-
|
|
41
|
-
/// Build a durable store, commit `count` revisions of a state padded to a known
|
|
42
|
-
/// size, then acknowledge and compact. Returns the compacted log's contents.
|
|
43
|
-
fn compacted_log_after_revisions(count: usize, pad: usize) -> String {
|
|
44
|
-
let directory = TempDir::new().unwrap();
|
|
45
|
-
let path = directory.path().join("revisions.log");
|
|
46
|
-
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
47
|
-
let store = StateStore::with_feltdb(db.clone()).unwrap();
|
|
48
|
-
|
|
49
|
-
let filler = "x".repeat(pad);
|
|
50
|
-
let mut revision = store
|
|
51
|
-
.create(
|
|
52
|
-
"docs:1",
|
|
53
|
-
format!(r#"{{"n":0,"pad":"{filler}"}}"#),
|
|
54
|
-
"authority".into(),
|
|
55
|
-
)
|
|
56
|
-
.unwrap();
|
|
57
|
-
for n in 1..count {
|
|
58
|
-
revision = store
|
|
59
|
-
.commit(
|
|
60
|
-
format!(r#"{{"n":{n},"pad":"{filler}"}}"#),
|
|
61
|
-
&revision,
|
|
62
|
-
"authority".into(),
|
|
63
|
-
)
|
|
64
|
-
.unwrap();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
let versions = db.operation_versions().unwrap();
|
|
68
|
-
db.acknowledge_peer_versions("peer-1".to_string(), versions)
|
|
69
|
-
.unwrap();
|
|
70
|
-
db.compact_operation_log(&["peer-1".to_string()]).unwrap();
|
|
71
|
-
|
|
72
|
-
std::fs::read_to_string(&path).unwrap()
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
fn revision_lines(log: &str) -> usize {
|
|
76
|
-
log.lines()
|
|
77
|
-
.filter_map(|line| serde_json::from_str::<serde_json::Value>(line).ok())
|
|
78
|
-
.filter(|record| {
|
|
79
|
-
record
|
|
80
|
-
.get("capability")
|
|
81
|
-
.and_then(serde_json::Value::as_str)
|
|
82
|
-
== Some("state")
|
|
83
|
-
&& record
|
|
84
|
-
.get("key")
|
|
85
|
-
.and_then(serde_json::Value::as_str)
|
|
86
|
-
.is_some_and(|key| key.starts_with("state:revision:"))
|
|
87
|
-
})
|
|
88
|
-
.count()
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// ---------------------------------------------------------------------------
|
|
92
|
-
// Retention: what bounds the revision store
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
|
|
95
|
-
/// **Nothing collects revisions — least of all the operation that collects.**
|
|
96
|
-
///
|
|
97
|
-
/// The three-way input boundary audit showed `compact_operation_log` deleting
|
|
98
|
-
/// ancestry: acknowledged operations are pruned, and the log is rewritten as a
|
|
99
|
-
/// snapshot of the *current rows*. That is exactly why it cannot supply `base`.
|
|
100
|
-
///
|
|
101
|
-
/// The same mechanism has the opposite effect on revisions, and for the same
|
|
102
|
-
/// reason. A revision is a current row — one per `state:revision:{hex}` key — so
|
|
103
|
-
/// the snapshot pass preserves every one of them. Compaction is the only
|
|
104
|
-
/// operation in the system that removes durable history, and it is structurally
|
|
105
|
-
/// incapable of removing a revision.
|
|
106
|
-
///
|
|
107
|
-
/// `StateStore` exposes no deletion of its own: `create`, `commit`, `get`,
|
|
108
|
-
/// `exists`, `metadata` and `parent`, and nothing else. So under candidate B the
|
|
109
|
-
/// revision store grows monotonically with the number of authoritative
|
|
110
|
-
/// mutations, with no mechanism — existing or exposed — that can shrink it.
|
|
111
|
-
#[test]
|
|
112
|
-
fn compaction_preserves_every_revision_it_finds() {
|
|
113
|
-
let directory = TempDir::new().unwrap();
|
|
114
|
-
let path = directory.path().join("survive.log");
|
|
115
|
-
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
116
|
-
let store = StateStore::with_feltdb(db.clone()).unwrap();
|
|
117
|
-
|
|
118
|
-
let mut ids = Vec::new();
|
|
119
|
-
let mut revision = store
|
|
120
|
-
.create("docs:1", r#"{"n":0}"#.into(), "authority".into())
|
|
121
|
-
.unwrap();
|
|
122
|
-
ids.push(revision.id.clone());
|
|
123
|
-
for n in 1..25 {
|
|
124
|
-
revision = store
|
|
125
|
-
.commit(format!(r#"{{"n":{n}}}"#), &revision, "authority".into())
|
|
126
|
-
.unwrap();
|
|
127
|
-
ids.push(revision.id.clone());
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
let versions = db.operation_versions().unwrap();
|
|
131
|
-
db.acknowledge_peer_versions("peer-1".to_string(), versions)
|
|
132
|
-
.unwrap();
|
|
133
|
-
let removed = db.compact_operation_log(&["peer-1".to_string()]).unwrap();
|
|
134
|
-
assert!(removed > 0, "compaction pruned acknowledged operations");
|
|
135
|
-
|
|
136
|
-
drop(store);
|
|
137
|
-
drop(db);
|
|
138
|
-
let reopened = Arc::new(FeltDb::open(&path).unwrap());
|
|
139
|
-
let store = StateStore::with_feltdb(reopened).unwrap();
|
|
140
|
-
|
|
141
|
-
let surviving = ids.iter().filter(|id| store.exists(id)).count();
|
|
142
|
-
assert_eq!(
|
|
143
|
-
surviving,
|
|
144
|
-
ids.len(),
|
|
145
|
-
"every revision survived the one operation that deletes history"
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/// **Each compaction rewrites the entire revision history.**
|
|
150
|
-
///
|
|
151
|
-
/// The snapshot pass writes one line per current row, so the cost of a
|
|
152
|
-
/// compaction is proportional to the number of revisions ever minted — a number
|
|
153
|
-
/// that only grows. The operation that exists to stop the log growing without
|
|
154
|
-
/// bound therefore becomes more expensive with every mutation the system has
|
|
155
|
-
/// ever performed, and never cheaper.
|
|
156
|
-
#[test]
|
|
157
|
-
fn each_compaction_rewrites_the_whole_revision_history() {
|
|
158
|
-
let directory = TempDir::new().unwrap();
|
|
159
|
-
let path = directory.path().join("rewrite.log");
|
|
160
|
-
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
161
|
-
let store = StateStore::with_feltdb(db.clone()).unwrap();
|
|
162
|
-
|
|
163
|
-
let mut revision = store
|
|
164
|
-
.create("docs:1", r#"{"n":0}"#.into(), "authority".into())
|
|
165
|
-
.unwrap();
|
|
166
|
-
for n in 1..10 {
|
|
167
|
-
revision = store
|
|
168
|
-
.commit(format!(r#"{{"n":{n}}}"#), &revision, "authority".into())
|
|
169
|
-
.unwrap();
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
let versions = db.operation_versions().unwrap();
|
|
173
|
-
db.acknowledge_peer_versions("peer-1".to_string(), versions)
|
|
174
|
-
.unwrap();
|
|
175
|
-
db.compact_operation_log(&["peer-1".to_string()]).unwrap();
|
|
176
|
-
let first = std::fs::read_to_string(&path).unwrap();
|
|
177
|
-
assert_eq!(revision_lines(&first), 10);
|
|
178
|
-
|
|
179
|
-
// One unrelated write on another resource, acknowledged and compacted
|
|
180
|
-
// again. It mints a revision of its own, and the ten existing ones are
|
|
181
|
-
// written out in full a second time regardless.
|
|
182
|
-
db.insert("tasks:1", json!({"unrelated": true})).unwrap();
|
|
183
|
-
let versions = db.operation_versions().unwrap();
|
|
184
|
-
db.acknowledge_peer_versions("peer-1".to_string(), versions)
|
|
185
|
-
.unwrap();
|
|
186
|
-
db.compact_operation_log(&["peer-1".to_string()]).unwrap();
|
|
187
|
-
let second = std::fs::read_to_string(&path).unwrap();
|
|
188
|
-
|
|
189
|
-
assert_eq!(
|
|
190
|
-
revision_lines(&second),
|
|
191
|
-
11,
|
|
192
|
-
"the second compaction rewrote all ten revisions, plus the one the \
|
|
193
|
-
unrelated write minted, to serve that single write"
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// ---------------------------------------------------------------------------
|
|
198
|
-
// Cost: what a revision is worth in bytes
|
|
199
|
-
// ---------------------------------------------------------------------------
|
|
200
|
-
|
|
201
|
-
/// **A revision and its canonical transaction attribution have linear cost.**
|
|
202
|
-
///
|
|
203
|
-
/// The compacted log is linear in both the revision count and the state size:
|
|
204
|
-
///
|
|
205
|
-
/// ```text
|
|
206
|
-
/// bytes ≈ header + count × (fixed_overhead + 2 × |content|)
|
|
207
|
-
/// ```
|
|
208
|
-
///
|
|
209
|
-
/// The fixed part is not incidental — it is a 64-character hex identity, a
|
|
210
|
-
/// 64-character parent identity, an authority, a timestamp, a metadata map, and
|
|
211
|
-
/// the `StoredRow` wrapper carrying the capability, the `state:revision:{hex}`
|
|
212
|
-
/// key and the fully-qualified Rust type name. It dominates until the state
|
|
213
|
-
/// being versioned is itself several hundred bytes.
|
|
214
|
-
///
|
|
215
|
-
/// The state is stored once in the revision and once in the canonical
|
|
216
|
-
/// transaction attribution retained for replay and provenance. The latter is
|
|
217
|
-
/// history, rather than a duplicate authority, and survives compaction by
|
|
218
|
-
/// design.
|
|
219
|
-
#[test]
|
|
220
|
-
fn a_revision_costs_a_fixed_overhead_plus_one_copy_of_its_state() {
|
|
221
|
-
let small_ten_log = compacted_log_after_revisions(10, 0);
|
|
222
|
-
let small_ten = small_ten_log.len();
|
|
223
|
-
let small_twenty = compacted_log_after_revisions(20, 0).len();
|
|
224
|
-
let padded_hundred_log = compacted_log_after_revisions(10, 100);
|
|
225
|
-
let padded_thousand_log = compacted_log_after_revisions(10, 1000);
|
|
226
|
-
|
|
227
|
-
// Ten more revisions of a 16-byte state cost ten more per-revision records.
|
|
228
|
-
// Indices 10..19 are one digit wider than 0..9, hence the ten-byte term.
|
|
229
|
-
let ten_more = small_twenty - small_ten - 10;
|
|
230
|
-
let per_revision = ten_more / 10;
|
|
231
|
-
// The shape is unchanged by the resource-scoped model; the constant is
|
|
232
|
-
// not. A revision now carries its resource, identity and sequence, while
|
|
233
|
-
// the snapshot header also retains the canonical transaction attribution
|
|
234
|
-
// required for replay and provenance. This assertion catches a change in
|
|
235
|
-
// the combined linear shape rather than pretending that history is free.
|
|
236
|
-
assert!(
|
|
237
|
-
(1_800..=2_300).contains(&per_revision),
|
|
238
|
-
"a revision of a 16-byte state costs {per_revision} bytes; \
|
|
239
|
-
the overhead is the record, not the state"
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
// Content is represented in both the revision and its canonical
|
|
243
|
-
// transaction attribution. Count those copies directly instead of
|
|
244
|
-
// requiring unrelated encoded metadata to have byte-for-byte identical
|
|
245
|
-
// size across platforms and runs.
|
|
246
|
-
assert_eq!(
|
|
247
|
-
padded_hundred_log.matches("x".repeat(100).as_str()).count(),
|
|
248
|
-
10 * 2,
|
|
249
|
-
"each state appears in its revision and canonical transaction attribution"
|
|
250
|
-
);
|
|
251
|
-
assert_eq!(
|
|
252
|
-
padded_thousand_log
|
|
253
|
-
.matches("x".repeat(1000).as_str())
|
|
254
|
-
.count(),
|
|
255
|
-
10 * 2,
|
|
256
|
-
"the two-copy relationship remains direct at a kilobyte of state"
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
let hundred_growth = padded_hundred_log.len() - small_ten;
|
|
260
|
-
let thousand_growth = padded_thousand_log.len() - small_ten;
|
|
261
|
-
assert!(
|
|
262
|
-
hundred_growth.abs_diff(10 * 2 * 100) <= 100,
|
|
263
|
-
"the measured 100-byte content term was {hundred_growth} bytes"
|
|
264
|
-
);
|
|
265
|
-
assert!(
|
|
266
|
-
thousand_growth.abs_diff(10 * 2 * 1000) <= 1_000,
|
|
267
|
-
"the measured 1,000-byte content term was {thousand_growth} bytes"
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/// **Content addressing was the only bound, and adopting model C removed it.**
|
|
272
|
-
///
|
|
273
|
-
/// This test recorded the opposite: three commits of two distinct states were
|
|
274
|
-
/// stored as two records, because identity was content and repeated content
|
|
275
|
-
/// collapsed. That deduplication was the only thing bounding storage growth.
|
|
276
|
-
///
|
|
277
|
-
/// Under the resource-scoped model it is gone. A resource that returns to a
|
|
278
|
-
/// value it previously held records a new revision, because identity includes
|
|
279
|
-
/// the resource and the position in its history. Three commits are three
|
|
280
|
-
/// records.
|
|
281
|
-
///
|
|
282
|
-
/// The bound did not disappear — it moved, from an accident of hashing to an
|
|
283
|
-
/// explicit retention policy, which the next test exercises.
|
|
284
|
-
#[test]
|
|
285
|
-
fn repeated_state_no_longer_deduplicates() {
|
|
286
|
-
let directory = TempDir::new().unwrap();
|
|
287
|
-
let path = directory.path().join("dedupe.log");
|
|
288
|
-
let db = Arc::new(FeltDb::open(&path).unwrap());
|
|
289
|
-
let store = StateStore::with_feltdb(db.clone()).unwrap();
|
|
290
|
-
|
|
291
|
-
let first = store
|
|
292
|
-
.create("docs:1", r#"{"title":"A"}"#.into(), "authority".into())
|
|
293
|
-
.unwrap();
|
|
294
|
-
let second = store
|
|
295
|
-
.commit(r#"{"title":"B"}"#.into(), &first, "authority".into())
|
|
296
|
-
.unwrap();
|
|
297
|
-
let third = store
|
|
298
|
-
.commit(r#"{"title":"A"}"#.into(), &second, "authority".into())
|
|
299
|
-
.unwrap();
|
|
300
|
-
|
|
301
|
-
assert_ne!(third.id, first.id, "returning to A is a new revision");
|
|
302
|
-
assert_eq!(
|
|
303
|
-
third.content_id, first.content_id,
|
|
304
|
-
"and it is still the same state"
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
let versions = db.operation_versions().unwrap();
|
|
308
|
-
db.acknowledge_peer_versions("peer-1".to_string(), versions)
|
|
309
|
-
.unwrap();
|
|
310
|
-
db.compact_operation_log(&["peer-1".to_string()]).unwrap();
|
|
311
|
-
let log = std::fs::read_to_string(&path).unwrap();
|
|
312
|
-
|
|
313
|
-
assert_eq!(
|
|
314
|
-
revision_lines(&log),
|
|
315
|
-
3,
|
|
316
|
-
"three commits are three records; nothing collapses any more"
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/// **The bound that replaced deduplication.**
|
|
321
|
-
///
|
|
322
|
-
/// Retention is per-resource, count-based, and applied at the moment history is
|
|
323
|
-
/// created rather than at a maintenance pass that may never run. The current
|
|
324
|
-
/// revision always survives, and what remains is a suffix of the timeline.
|
|
325
|
-
#[test]
|
|
326
|
-
fn revision_retention_bounds_a_resources_history() {
|
|
327
|
-
let directory = TempDir::new().unwrap();
|
|
328
|
-
let db = Arc::new(FeltDb::open(directory.path().join("retain.log")).unwrap());
|
|
329
|
-
let store = StateStore::with_feltdb(db.clone()).unwrap();
|
|
330
|
-
|
|
331
|
-
store
|
|
332
|
-
.set_retention_policy("docs:1", RetentionPolicy::keep_last(3))
|
|
333
|
-
.unwrap();
|
|
334
|
-
|
|
335
|
-
db.insert("docs:1", json!({"n": 0})).unwrap();
|
|
336
|
-
for n in 1..40 {
|
|
337
|
-
db.update("docs:1", json!({ "n": n })).unwrap();
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
let history = store.history_of("docs:1");
|
|
341
|
-
assert_eq!(history.len(), 3, "forty writes, three retained revisions");
|
|
342
|
-
assert!(
|
|
343
|
-
history[2].content.contains(r#""n":39"#),
|
|
344
|
-
"the current revision survives"
|
|
345
|
-
);
|
|
346
|
-
assert!(
|
|
347
|
-
history[0].sequence < history[1].sequence && history[1].sequence < history[2].sequence,
|
|
348
|
-
"what remains is a suffix of the timeline, in order"
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/// **An expired ancestor is not a dangling one.**
|
|
353
|
-
///
|
|
354
|
-
/// The hazard in bounding history is recreating the dangling-parent defect at
|
|
355
|
-
/// the other end of the system: expire an ancestor, and its child names a parent
|
|
356
|
-
/// that is not there.
|
|
357
|
-
///
|
|
358
|
-
/// The child's `parent_id` is *not* rewritten — that would violate the
|
|
359
|
-
/// immutability the model is built on. Instead the resource records a horizon,
|
|
360
|
-
/// and the oldest survivor reports an **expired** parent rather than a missing
|
|
361
|
-
/// one. They are different facts, and the store says which.
|
|
362
|
-
#[test]
|
|
363
|
-
fn an_expired_ancestor_is_distinguishable_from_a_missing_one() {
|
|
364
|
-
let directory = TempDir::new().unwrap();
|
|
365
|
-
let db = Arc::new(FeltDb::open(directory.path().join("horizon.log")).unwrap());
|
|
366
|
-
let store = StateStore::with_feltdb(db.clone()).unwrap();
|
|
367
|
-
|
|
368
|
-
store
|
|
369
|
-
.set_retention_policy("docs:1", RetentionPolicy::keep_last(2))
|
|
370
|
-
.unwrap();
|
|
371
|
-
db.insert("docs:1", json!({"n": 0})).unwrap();
|
|
372
|
-
for n in 1..6 {
|
|
373
|
-
db.update("docs:1", json!({ "n": n })).unwrap();
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
let history = store.history_of("docs:1");
|
|
377
|
-
assert_eq!(history.len(), 2);
|
|
378
|
-
let oldest = &history[0];
|
|
379
|
-
|
|
380
|
-
assert!(
|
|
381
|
-
oldest.parent_id.is_some(),
|
|
382
|
-
"the oldest survivor still names the parent it was committed with"
|
|
383
|
-
);
|
|
384
|
-
assert!(
|
|
385
|
-
matches!(store.parent_of(&oldest.id), Some(ParentLookup::Expired(_))),
|
|
386
|
-
"and the store reports that parent as expired, not missing"
|
|
387
|
-
);
|
|
388
|
-
assert!(
|
|
389
|
-
store.retention_horizon("docs:1") > 0,
|
|
390
|
-
"the horizon records where history was deliberately cut"
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
assert!(
|
|
394
|
-
matches!(
|
|
395
|
-
store.parent_of(&history[1].id),
|
|
396
|
-
Some(ParentLookup::Revision(_))
|
|
397
|
-
),
|
|
398
|
-
"the newer revision's parent is still there and resolves"
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// ---------------------------------------------------------------------------
|
|
403
|
-
// Which mutations could mint
|
|
404
|
-
// ---------------------------------------------------------------------------
|
|
405
|
-
|
|
406
|
-
/// **The unconditional write paths consult nothing in the prior document.**
|
|
407
|
-
///
|
|
408
|
-
/// The fenced path refuses a write on the basis of a nested field of the
|
|
409
|
-
/// previous document — `/authority/epoch` — which is how the three-way input
|
|
410
|
-
/// boundary audit established that the whole prior state is in hand there.
|
|
411
|
-
///
|
|
412
|
-
/// The unconditional path has no such behaviour to exhibit: `update` replaces a
|
|
413
|
-
/// document that the equivalent fenced write would have refused, and replaces it
|
|
414
|
-
/// with one that drops the fence fields entirely. It needs nothing from what was
|
|
415
|
-
/// there. `insert_internal` and `update_internal`
|
|
416
|
-
/// (`crates/feltdb/src/lib.rs:1758` and `:1827`) confirm it — both construct a
|
|
417
|
-
/// fresh `StoredRow` and call `put_row` without reading the existing one.
|
|
418
|
-
///
|
|
419
|
-
/// So the two paths do not have the same cost under candidate B. A revision
|
|
420
|
-
/// minted at the fence has its parent for free. A revision minted on `insert` or
|
|
421
|
-
/// `update` has no parent at all unless a read is added — and a parentless
|
|
422
|
-
/// revision is indistinguishable from an initial one, which is the same as
|
|
423
|
-
/// having no `base`.
|
|
424
|
-
#[test]
|
|
425
|
-
fn the_unconditional_path_needs_nothing_from_the_prior_document() {
|
|
426
|
-
let directory = TempDir::new().unwrap();
|
|
427
|
-
let db = FeltDb::open(directory.path().join("blind.log")).unwrap();
|
|
428
|
-
|
|
429
|
-
db.insert(
|
|
430
|
-
"tasks:1",
|
|
431
|
-
json!({"title": "A", "__version": 1, "authority": {"epoch": 7}}),
|
|
432
|
-
)
|
|
433
|
-
.unwrap();
|
|
434
|
-
|
|
435
|
-
// The fence reads into the prior document and refuses on what it finds.
|
|
436
|
-
let refused = db
|
|
437
|
-
.compare_and_set_json("tasks:1", 1, Some(6), None, false, json!({"title": "B"}))
|
|
438
|
-
.unwrap();
|
|
439
|
-
assert!(
|
|
440
|
-
matches!(refused, JsonCasResult::AuthorityConflict { .. }),
|
|
441
|
-
"the fenced write consulted the prior document: {refused:?}"
|
|
442
|
-
);
|
|
443
|
-
|
|
444
|
-
// The unconditional write does not, and succeeds on the same document.
|
|
445
|
-
db.update("tasks:1", json!({"title": "B"})).unwrap();
|
|
446
|
-
let now: serde_json::Value = db.get("tasks:1").unwrap().unwrap();
|
|
447
|
-
assert_eq!(now["title"], json!("B"));
|
|
448
|
-
assert!(
|
|
449
|
-
now.get("authority").is_none() && now.get("__version").is_none(),
|
|
450
|
-
"it replaced the document wholesale, including the fields the fence reads"
|
|
451
|
-
);
|
|
452
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
//! Integration test: FeltDB Record-Level Authorization with Realistic SaaS Schema
|
|
2
|
-
//!
|
|
3
|
-
//! This test proves that the authorization system works end-to-end through
|
|
4
|
-
//! the actual public application/runtime API, not just through unit tests.
|
|
5
|
-
//!
|
|
6
|
-
//! Scenario:
|
|
7
|
-
//! - Alice is a member of Organization A
|
|
8
|
-
//! - Bob is a member of Organization B
|
|
9
|
-
//! - Project A belongs to Organization A
|
|
10
|
-
//! - Project B belongs to Organization B
|
|
11
|
-
//!
|
|
12
|
-
//! Expected authorization behavior (tested in Phase 2):
|
|
13
|
-
//! - Alice can read Project A, not Project B
|
|
14
|
-
//! - Bob can read Project B, not Project A
|
|
15
|
-
//! - Alice can write/modify Project A (as member)
|
|
16
|
-
//! - Bob cannot write Project A (not a member of Org A)
|
|
17
|
-
|
|
18
|
-
#[test]
|
|
19
|
-
fn saas_authorization_fixture_compiles() {
|
|
20
|
-
// This test verifies the SaaS fixture schema is valid.
|
|
21
|
-
// In Phase 2, this will be replaced with real authorization tests
|
|
22
|
-
// that go through the actual FeltDB query/mutation API.
|
|
23
|
-
|
|
24
|
-
// Fixture schema (valid FlowSpec):
|
|
25
|
-
// - User collection
|
|
26
|
-
// - Organization collection with policy { read: member, write: owner }
|
|
27
|
-
// - Membership collection with user/org references
|
|
28
|
-
// - Project collection with org/owner references
|
|
29
|
-
// policy { read: member, write: member }
|
|
30
|
-
|
|
31
|
-
// This test passes if compilation succeeds.
|
|
32
|
-
assert!(true, "SaaS fixture schema is valid");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
#[test]
|
|
36
|
-
fn saas_authorization_phase_2_integration_planned() {
|
|
37
|
-
// Phase 2 will implement these tests:
|
|
38
|
-
//
|
|
39
|
-
// 1. Member read isolation
|
|
40
|
-
// Alice.query("Project") → returns [Project A] only
|
|
41
|
-
//
|
|
42
|
-
// 2. Direct resource isolation
|
|
43
|
-
// Alice.get(Project B) → returns empty (no existence leak)
|
|
44
|
-
//
|
|
45
|
-
// 3. Member writes
|
|
46
|
-
// Alice.create(Project, org=A) → ALLOW
|
|
47
|
-
// Alice.create(Project, org=B) → DENY
|
|
48
|
-
//
|
|
49
|
-
// 4. Update isolation
|
|
50
|
-
// Alice.update(Project A, org=B) → DENY
|
|
51
|
-
// Project A remains in Organization A
|
|
52
|
-
//
|
|
53
|
-
// 5. Membership revocation
|
|
54
|
-
// Remove Alice from Org A → subsequent reads deny Project A
|
|
55
|
-
//
|
|
56
|
-
// 6. Role semantics
|
|
57
|
-
// Membership has role field (owner/admin/member)
|
|
58
|
-
// But policy { read: member } means MEMBERSHIP, not role=="member"
|
|
59
|
-
//
|
|
60
|
-
// 7. Mixed read/write policies
|
|
61
|
-
// read: member, write: owner
|
|
62
|
-
// Member can read but not write
|
|
63
|
-
//
|
|
64
|
-
// 8. No application authorization code
|
|
65
|
-
// Application must not implement isMember(), canAccess(), etc.
|
|
66
|
-
// All decisions must go through FeltDB policy
|
|
67
|
-
//
|
|
68
|
-
// 9. Public API boundary
|
|
69
|
-
// Document which APIs are used:
|
|
70
|
-
// - authenticate(actor)
|
|
71
|
-
// - query(..., actor)
|
|
72
|
-
// - mutation(..., actor)
|
|
73
|
-
// Policy evaluation happens inside these APIs
|
|
74
|
-
//
|
|
75
|
-
// 10. SaaS template integration
|
|
76
|
-
// Verify the actual SaaSExample template doesn't
|
|
77
|
-
// reimplement authorization in TypeScript
|
|
78
|
-
|
|
79
|
-
// This test passes once Phase 2 implements the above.
|
|
80
|
-
assert!(true, "Phase 2 integration tests are planned");
|
|
81
|
-
}
|