@feltdb/core 0.8.4 → 0.8.5
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/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +165 -0
- package/dist/create/server-source/Cargo.toml +9 -0
- package/dist/create/server-source/crates/feltdb/Cargo.toml +3 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +44 -44
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +12 -24
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +7 -13
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +13 -10
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +12 -9
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +78 -25
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +16 -13
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +13 -5
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +41 -22
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +33 -15
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +100 -33
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +56 -20
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +116 -41
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +186 -55
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +64 -26
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +46 -15
- package/dist/create/server-source/crates/feltdb/src/admission.rs +8 -15
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +43 -13
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +15 -42
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +65 -19
- package/dist/create/server-source/crates/feltdb/src/application.rs +113 -30
- package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +475 -140
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +17 -15
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -1
- package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +59 -15
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +266 -114
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +25 -7
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +10 -15
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +3 -11
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +14 -5
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +21 -25
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +14 -11
- package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +103 -22
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +18 -15
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +12 -8
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +1 -5
- package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +87 -23
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +3 -7
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +10 -9
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +1 -6
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +35 -38
- package/dist/create/server-source/crates/feltdb/src/lib.rs +507 -6
- package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +4 -1
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -1
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +1 -2
- package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +107 -30
- package/dist/create/server-source/crates/feltdb/src/observability.rs +19 -6
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +12 -11
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +9 -4
- package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +65 -18
- package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +193 -57
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +37 -27
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +38 -10
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +20 -14
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +394 -229
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +8 -6
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +11 -13
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +79 -70
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +3 -3
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +33 -11
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +6 -6
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +14 -56
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +29 -27
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +35 -17
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +8 -2
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +59 -15
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +70 -17
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +23 -6
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +12 -3
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +41 -11
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +56 -25
- package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +701 -245
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +31 -13
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +6 -8
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +5 -5
- package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +48 -18
- package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +30 -11
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +6 -3
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +4 -3
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +36 -10
- package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +516 -0
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +13 -4
- package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +222 -0
- package/dist/create/server-source/crates/feltdb/src/state_facade.rs +82 -54
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +2 -2
- package/dist/create/server-source/crates/feltdb/src/state_model.rs +1514 -537
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +6 -3
- package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +672 -0
- package/dist/create/server-source/crates/feltdb/src/submission.rs +5 -11
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +6 -8
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +24 -35
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +24 -8
- package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +248 -59
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +17 -20
- package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +749 -0
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +1 -0
- package/dist/create/server-source/crates/feltdb/src/workload.rs +512 -4
- package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +3 -4
- package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +288 -0
- package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +392 -0
- package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +436 -220
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +1916 -0
- package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +1878 -0
- package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +1862 -0
- package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +2 -1
- package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +80 -25
- package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +5 -8
- package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +52 -44
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +33 -16
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +22 -7
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +41 -22
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +25 -8
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +9 -6
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +120 -69
- package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +21 -10
- package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +1032 -0
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +868 -0
- package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +221 -0
- package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +338 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +1039 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +739 -0
- package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +427 -0
- package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +3 -3
- package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +25 -22
- package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +1799 -0
- package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +1316 -0
- package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +53 -61
- package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +156 -61
- package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +299 -0
- package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +255 -0
- package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +249 -0
- package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +994 -0
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +2 -1
- package/dist/create/server-source/crates/feltdb-server/src/auth.rs +164 -13
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +326 -6
- package/dist/studio-app/assets/{feltdb_wasm-CVQWgXO-.js → feltdb_wasm-DaNwCLRX.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-DnsHNv6g.wasm +0 -0
- package/dist/studio-app/assets/{index-DwgNAIIX.js → index-j8IlhNqJ.js} +1 -1
- package/dist/studio-app/index.html +1 -1
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/workload.d.ts +2 -1
- package/dist/workload.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-CNVpvaZV.wasm +0 -0
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
#[cfg(test)]
|
|
10
10
|
mod feltdb_state_boundary_tests {
|
|
11
11
|
use feltdb::{
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
ConflictClass,
|
|
12
|
+
ConflictClass, ConflictClassification, FeltDBStateSystem, Relationship, SemanticDiff,
|
|
13
|
+
StateTopology,
|
|
15
14
|
};
|
|
16
15
|
use serde_json::json;
|
|
17
16
|
|
|
@@ -22,21 +21,32 @@ mod feltdb_state_boundary_tests {
|
|
|
22
21
|
#[test]
|
|
23
22
|
fn boundary_state_id_deterministic() {
|
|
24
23
|
let store = FeltDBStateSystem::create_test_store();
|
|
25
|
-
|
|
26
|
-
let state1 = store
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
|
|
25
|
+
let state1 = store
|
|
26
|
+
.create("docs:1", r#"{"x":1}"#.to_string(), "auth1".to_string())
|
|
27
|
+
.unwrap();
|
|
28
|
+
let state2 = store
|
|
29
|
+
.create("docs:2", r#"{"x":1}"#.to_string(), "auth2".to_string())
|
|
30
|
+
.unwrap();
|
|
31
|
+
|
|
32
|
+
// Same content on two resources is the same *state* and two distinct
|
|
33
|
+
// *revisions*. Content identity is deterministic; revision identity
|
|
34
|
+
// names the occurrence, and an occurrence belongs to a resource.
|
|
35
|
+
assert_eq!(state1.content_id, state2.content_id);
|
|
36
|
+
assert_ne!(state1.id, state2.id);
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
#[test]
|
|
34
40
|
fn boundary_state_id_representation_sensitive() {
|
|
35
41
|
let store = FeltDBStateSystem::create_test_store();
|
|
36
|
-
|
|
37
|
-
let string_state = store
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
|
|
43
|
+
let string_state = store
|
|
44
|
+
.create("docs:3", r#"{"v":"1"}"#.to_string(), "auth".to_string())
|
|
45
|
+
.unwrap();
|
|
46
|
+
let number_state = store
|
|
47
|
+
.create("docs:4", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
48
|
+
.unwrap();
|
|
49
|
+
|
|
40
50
|
// "1" ≠ 1 in JSON representation
|
|
41
51
|
assert_ne!(string_state.id, number_state.id);
|
|
42
52
|
}
|
|
@@ -44,13 +54,25 @@ mod feltdb_state_boundary_tests {
|
|
|
44
54
|
#[test]
|
|
45
55
|
fn boundary_revision_immutability() {
|
|
46
56
|
let store = FeltDBStateSystem::create_test_store();
|
|
47
|
-
let rev1 = store
|
|
48
|
-
|
|
57
|
+
let rev1 = store
|
|
58
|
+
.create(
|
|
59
|
+
"docs:5",
|
|
60
|
+
r#"{"data":"initial"}"#.to_string(),
|
|
61
|
+
"auth".to_string(),
|
|
62
|
+
)
|
|
63
|
+
.unwrap();
|
|
64
|
+
|
|
49
65
|
let rev1_id = rev1.id.clone();
|
|
50
|
-
|
|
66
|
+
|
|
51
67
|
// Create another state
|
|
52
|
-
let _rev2 = store
|
|
53
|
-
|
|
68
|
+
let _rev2 = store
|
|
69
|
+
.commit(
|
|
70
|
+
r#"{"data":"modified"}"#.to_string(),
|
|
71
|
+
&rev1,
|
|
72
|
+
"auth".to_string(),
|
|
73
|
+
)
|
|
74
|
+
.unwrap();
|
|
75
|
+
|
|
54
76
|
// First revision is unchanged
|
|
55
77
|
let retrieved = store.get(&rev1_id).unwrap();
|
|
56
78
|
assert_eq!(retrieved.id, rev1_id);
|
|
@@ -64,10 +86,16 @@ mod feltdb_state_boundary_tests {
|
|
|
64
86
|
#[test]
|
|
65
87
|
fn boundary_state_persistence() {
|
|
66
88
|
let store = FeltDBStateSystem::create_test_store();
|
|
67
|
-
let state = store
|
|
68
|
-
|
|
89
|
+
let state = store
|
|
90
|
+
.create(
|
|
91
|
+
"docs:6",
|
|
92
|
+
r#"{"persistent":"data"}"#.to_string(),
|
|
93
|
+
"auth".to_string(),
|
|
94
|
+
)
|
|
95
|
+
.unwrap();
|
|
96
|
+
|
|
69
97
|
let state_id = state.id.clone();
|
|
70
|
-
|
|
98
|
+
|
|
71
99
|
// Verify it persists in store
|
|
72
100
|
assert!(store.exists(&state_id));
|
|
73
101
|
assert_eq!(store.get(&state_id).unwrap().id, state_id);
|
|
@@ -76,13 +104,17 @@ mod feltdb_state_boundary_tests {
|
|
|
76
104
|
#[test]
|
|
77
105
|
fn boundary_current_pointer_persistence() {
|
|
78
106
|
let store = FeltDBStateSystem::create_test_store();
|
|
79
|
-
|
|
80
|
-
let state1 = store
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
107
|
+
|
|
108
|
+
let state1 = store
|
|
109
|
+
.create("docs:7", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
110
|
+
.unwrap();
|
|
111
|
+
assert_eq!(store.get(&state1.id).unwrap().id, state1.id);
|
|
112
|
+
|
|
113
|
+
let state2 = store
|
|
114
|
+
.commit(r#"{"v":2}"#.to_string(), &state1, "auth".to_string())
|
|
115
|
+
.unwrap();
|
|
116
|
+
assert_eq!(store.get(&state2.id).unwrap().id, state2.id);
|
|
117
|
+
|
|
86
118
|
// Verify both exist
|
|
87
119
|
assert!(store.exists(&state1.id));
|
|
88
120
|
assert!(store.exists(&state2.id));
|
|
@@ -95,11 +127,15 @@ mod feltdb_state_boundary_tests {
|
|
|
95
127
|
#[test]
|
|
96
128
|
fn boundary_expected_parent_validation() {
|
|
97
129
|
let store = FeltDBStateSystem::create_test_store();
|
|
98
|
-
let parent = store
|
|
99
|
-
|
|
130
|
+
let parent = store
|
|
131
|
+
.create("docs:8", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
132
|
+
.unwrap();
|
|
133
|
+
|
|
100
134
|
// Create child with explicit parent
|
|
101
|
-
let child = store
|
|
102
|
-
|
|
135
|
+
let child = store
|
|
136
|
+
.commit(r#"{"v":2}"#.to_string(), &parent, "auth".to_string())
|
|
137
|
+
.unwrap();
|
|
138
|
+
|
|
103
139
|
// Child must have parent
|
|
104
140
|
assert_eq!(child.parent_id, Some(parent.id.clone()));
|
|
105
141
|
}
|
|
@@ -107,21 +143,27 @@ mod feltdb_state_boundary_tests {
|
|
|
107
143
|
#[test]
|
|
108
144
|
fn boundary_stale_transition_rejection() {
|
|
109
145
|
let store = FeltDBStateSystem::create_test_store();
|
|
110
|
-
|
|
111
|
-
let state1 = store
|
|
112
|
-
|
|
113
|
-
|
|
146
|
+
|
|
147
|
+
let state1 = store
|
|
148
|
+
.create("docs:9", r#"{"seq":1}"#.to_string(), "auth".to_string())
|
|
149
|
+
.unwrap();
|
|
150
|
+
let state2 = store
|
|
151
|
+
.commit(r#"{"seq":2}"#.to_string(), &state1, "auth".to_string())
|
|
152
|
+
.unwrap();
|
|
153
|
+
|
|
114
154
|
// Create a branch from state1 (before state2)
|
|
115
|
-
let branch = store
|
|
116
|
-
|
|
155
|
+
let branch = store
|
|
156
|
+
.commit(r#"{"seq":1.5}"#.to_string(), &state1, "auth".to_string())
|
|
157
|
+
.unwrap();
|
|
158
|
+
|
|
117
159
|
// Verify topology catches this divergence
|
|
118
160
|
let mut topology = StateTopology::new();
|
|
119
161
|
topology.add_revision(state1.clone());
|
|
120
162
|
topology.add_revision(state2.clone());
|
|
121
163
|
topology.add_revision(branch.clone());
|
|
122
|
-
|
|
164
|
+
|
|
123
165
|
match topology.relationship(&state2.id, &branch.id) {
|
|
124
|
-
Relationship::Diverged => {}
|
|
166
|
+
Relationship::Diverged => {} // Expected
|
|
125
167
|
_ => panic!("Expected Diverged relationship"),
|
|
126
168
|
}
|
|
127
169
|
}
|
|
@@ -129,16 +171,20 @@ mod feltdb_state_boundary_tests {
|
|
|
129
171
|
#[test]
|
|
130
172
|
fn boundary_revision_persistence_before_pointer_advancement() {
|
|
131
173
|
let store = FeltDBStateSystem::create_test_store();
|
|
132
|
-
|
|
133
|
-
let state1 = store
|
|
174
|
+
|
|
175
|
+
let state1 = store
|
|
176
|
+
.create("docs:10", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
177
|
+
.unwrap();
|
|
134
178
|
let state1_id = state1.id.clone();
|
|
135
|
-
|
|
179
|
+
|
|
136
180
|
// Create and advance current
|
|
137
|
-
let state2 = store
|
|
138
|
-
|
|
181
|
+
let state2 = store
|
|
182
|
+
.commit(r#"{"v":2}"#.to_string(), &state1, "auth".to_string())
|
|
183
|
+
.unwrap();
|
|
184
|
+
|
|
139
185
|
// Current points to state2
|
|
140
|
-
assert_eq!(store.
|
|
141
|
-
|
|
186
|
+
assert_eq!(store.get(&state2.id).unwrap().id, state2.id);
|
|
187
|
+
|
|
142
188
|
// But state1 still exists
|
|
143
189
|
assert!(store.exists(&state1_id));
|
|
144
190
|
assert_eq!(store.get(&state1_id).unwrap().id, state1_id);
|
|
@@ -151,68 +197,122 @@ mod feltdb_state_boundary_tests {
|
|
|
151
197
|
#[test]
|
|
152
198
|
fn boundary_explicit_parent_required() {
|
|
153
199
|
let store = FeltDBStateSystem::create_test_store();
|
|
154
|
-
let parent = store
|
|
155
|
-
|
|
200
|
+
let parent = store
|
|
201
|
+
.create(
|
|
202
|
+
"docs:11",
|
|
203
|
+
r#"{"parent":true}"#.to_string(),
|
|
204
|
+
"auth".to_string(),
|
|
205
|
+
)
|
|
206
|
+
.unwrap();
|
|
207
|
+
|
|
156
208
|
// Initial state has no parent
|
|
157
209
|
assert_eq!(parent.parent_id, None);
|
|
158
|
-
|
|
210
|
+
|
|
159
211
|
// Child must have parent
|
|
160
|
-
let child = store
|
|
212
|
+
let child = store
|
|
213
|
+
.commit(
|
|
214
|
+
r#"{"parent":true,"child":true}"#.to_string(),
|
|
215
|
+
&parent,
|
|
216
|
+
"auth".to_string(),
|
|
217
|
+
)
|
|
218
|
+
.unwrap();
|
|
161
219
|
assert_eq!(child.parent_id, Some(parent.id.clone()));
|
|
162
220
|
}
|
|
163
221
|
|
|
164
222
|
#[test]
|
|
165
|
-
fn
|
|
223
|
+
fn boundary_revisions_are_immutable_and_addressed_by_identity() {
|
|
166
224
|
let store = FeltDBStateSystem::create_test_store();
|
|
167
|
-
let state = store
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
225
|
+
let state = store
|
|
226
|
+
.create("docs:12", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
227
|
+
.unwrap();
|
|
228
|
+
|
|
229
|
+
// Committing a descendant does not disturb its parent. There is no
|
|
230
|
+
// named branch to create: the identity is what a name would point at.
|
|
231
|
+
let child = store
|
|
232
|
+
.commit(r#"{"v":2}"#.to_string(), &state, "auth".to_string())
|
|
233
|
+
.unwrap();
|
|
234
|
+
|
|
235
|
+
let original = store.get(&state.id).unwrap();
|
|
236
|
+
assert_eq!(original.id, state.id);
|
|
237
|
+
assert_eq!(original.content, r#"{"v":1}"#);
|
|
238
|
+
assert_eq!(store.parent(&child.id).unwrap().id, state.id);
|
|
176
239
|
}
|
|
177
240
|
|
|
178
241
|
#[test]
|
|
179
242
|
fn boundary_branch_does_not_advance_current() {
|
|
180
243
|
let store = FeltDBStateSystem::create_test_store();
|
|
181
|
-
let state1 = store
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
244
|
+
let state1 = store
|
|
245
|
+
.create(
|
|
246
|
+
"docs:13",
|
|
247
|
+
r#"{"current":"first"}"#.to_string(),
|
|
248
|
+
"auth".to_string(),
|
|
249
|
+
)
|
|
250
|
+
.unwrap();
|
|
251
|
+
|
|
252
|
+
let state2 = store
|
|
253
|
+
.commit(
|
|
254
|
+
r#"{"current":"second"}"#.to_string(),
|
|
255
|
+
&state1,
|
|
256
|
+
"auth".to_string(),
|
|
257
|
+
)
|
|
258
|
+
.unwrap();
|
|
259
|
+
assert_eq!(store.get(&state2.id).unwrap().id, state2.id);
|
|
260
|
+
|
|
261
|
+
// Both revisions remain independently addressable. Neither is
|
|
262
|
+
// privileged, because the store keeps no pointer that could privilege
|
|
263
|
+
// one of them.
|
|
264
|
+
assert_eq!(store.get(&state1.id).unwrap().id, state1.id);
|
|
265
|
+
assert_eq!(store.get(&state2.id).unwrap().id, state2.id);
|
|
191
266
|
}
|
|
192
267
|
|
|
193
268
|
#[test]
|
|
194
269
|
fn boundary_no_automatic_merge() {
|
|
195
270
|
let store = FeltDBStateSystem::create_test_store();
|
|
196
|
-
|
|
197
|
-
let initial = store
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
271
|
+
|
|
272
|
+
let initial = store
|
|
273
|
+
.create(
|
|
274
|
+
"docs:14",
|
|
275
|
+
r#"{"shared":100}"#.to_string(),
|
|
276
|
+
"auth".to_string(),
|
|
277
|
+
)
|
|
278
|
+
.unwrap();
|
|
279
|
+
|
|
280
|
+
let branch_a = store
|
|
281
|
+
.commit(
|
|
282
|
+
r#"{"shared":150}"#.to_string(),
|
|
283
|
+
&initial,
|
|
284
|
+
"auth".to_string(),
|
|
285
|
+
)
|
|
286
|
+
.unwrap();
|
|
287
|
+
let branch_b = store
|
|
288
|
+
.commit(
|
|
289
|
+
r#"{"shared":120}"#.to_string(),
|
|
290
|
+
&initial,
|
|
291
|
+
"auth".to_string(),
|
|
292
|
+
)
|
|
293
|
+
.unwrap();
|
|
294
|
+
|
|
202
295
|
// Two divergent branches exist
|
|
203
296
|
let mut topology = StateTopology::new();
|
|
204
297
|
topology.add_revision(initial.clone());
|
|
205
298
|
topology.add_revision(branch_a.clone());
|
|
206
299
|
topology.add_revision(branch_b.clone());
|
|
207
|
-
|
|
300
|
+
|
|
208
301
|
assert!(matches!(
|
|
209
302
|
topology.relationship(&branch_a.id, &branch_b.id),
|
|
210
303
|
Relationship::Diverged
|
|
211
304
|
));
|
|
212
|
-
|
|
213
|
-
// No merge happened automatically
|
|
214
|
-
|
|
215
|
-
|
|
305
|
+
|
|
306
|
+
// No merge happened automatically: both divergent revisions are still
|
|
307
|
+
// exactly what their authors wrote, and no third revision appeared.
|
|
308
|
+
assert_eq!(
|
|
309
|
+
store.get(&branch_a.id).unwrap().content,
|
|
310
|
+
r#"{"shared":150}"#
|
|
311
|
+
);
|
|
312
|
+
assert_eq!(
|
|
313
|
+
store.get(&branch_b.id).unwrap().content,
|
|
314
|
+
r#"{"shared":120}"#
|
|
315
|
+
);
|
|
216
316
|
}
|
|
217
317
|
|
|
218
318
|
// ========================================================================
|
|
@@ -222,16 +322,22 @@ mod feltdb_state_boundary_tests {
|
|
|
222
322
|
#[test]
|
|
223
323
|
fn boundary_ancestors_linear() {
|
|
224
324
|
let store = FeltDBStateSystem::create_test_store();
|
|
225
|
-
|
|
226
|
-
let s1 = store
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
325
|
+
|
|
326
|
+
let s1 = store
|
|
327
|
+
.create("docs:15", r#"{"seq":1}"#.to_string(), "auth".to_string())
|
|
328
|
+
.unwrap();
|
|
329
|
+
let s2 = store
|
|
330
|
+
.commit(r#"{"seq":2}"#.to_string(), &s1, "auth".to_string())
|
|
331
|
+
.unwrap();
|
|
332
|
+
let s3 = store
|
|
333
|
+
.commit(r#"{"seq":3}"#.to_string(), &s2, "auth".to_string())
|
|
334
|
+
.unwrap();
|
|
335
|
+
|
|
230
336
|
let mut topology = StateTopology::new();
|
|
231
337
|
topology.add_revision(s1.clone());
|
|
232
338
|
topology.add_revision(s2.clone());
|
|
233
339
|
topology.add_revision(s3.clone());
|
|
234
|
-
|
|
340
|
+
|
|
235
341
|
let ancestors = topology.ancestors(&s3.id);
|
|
236
342
|
assert!(ancestors.contains(&s2.id));
|
|
237
343
|
assert!(ancestors.contains(&s1.id));
|
|
@@ -241,16 +347,22 @@ mod feltdb_state_boundary_tests {
|
|
|
241
347
|
#[test]
|
|
242
348
|
fn boundary_is_ancestor() {
|
|
243
349
|
let store = FeltDBStateSystem::create_test_store();
|
|
244
|
-
|
|
245
|
-
let parent = store
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
350
|
+
|
|
351
|
+
let parent = store
|
|
352
|
+
.create("docs:16", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
353
|
+
.unwrap();
|
|
354
|
+
let child = store
|
|
355
|
+
.commit(r#"{"v":2}"#.to_string(), &parent, "auth".to_string())
|
|
356
|
+
.unwrap();
|
|
357
|
+
let grandchild = store
|
|
358
|
+
.commit(r#"{"v":3}"#.to_string(), &child, "auth".to_string())
|
|
359
|
+
.unwrap();
|
|
360
|
+
|
|
249
361
|
let mut topology = StateTopology::new();
|
|
250
362
|
topology.add_revision(parent.clone());
|
|
251
363
|
topology.add_revision(child.clone());
|
|
252
364
|
topology.add_revision(grandchild.clone());
|
|
253
|
-
|
|
365
|
+
|
|
254
366
|
assert!(topology.is_ancestor(&parent.id, &grandchild.id));
|
|
255
367
|
assert!(topology.is_ancestor(&child.id, &grandchild.id));
|
|
256
368
|
assert!(!topology.is_ancestor(&grandchild.id, &parent.id));
|
|
@@ -259,16 +371,26 @@ mod feltdb_state_boundary_tests {
|
|
|
259
371
|
#[test]
|
|
260
372
|
fn boundary_common_ancestor() {
|
|
261
373
|
let store = FeltDBStateSystem::create_test_store();
|
|
262
|
-
|
|
263
|
-
let root = store
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
374
|
+
|
|
375
|
+
let root = store
|
|
376
|
+
.create(
|
|
377
|
+
"docs:17",
|
|
378
|
+
r#"{"root":true}"#.to_string(),
|
|
379
|
+
"auth".to_string(),
|
|
380
|
+
)
|
|
381
|
+
.unwrap();
|
|
382
|
+
let left = store
|
|
383
|
+
.commit(r#"{"left":true}"#.to_string(), &root, "auth".to_string())
|
|
384
|
+
.unwrap();
|
|
385
|
+
let right = store
|
|
386
|
+
.commit(r#"{"right":true}"#.to_string(), &root, "auth".to_string())
|
|
387
|
+
.unwrap();
|
|
388
|
+
|
|
267
389
|
let mut topology = StateTopology::new();
|
|
268
390
|
topology.add_revision(root.clone());
|
|
269
391
|
topology.add_revision(left.clone());
|
|
270
392
|
topology.add_revision(right.clone());
|
|
271
|
-
|
|
393
|
+
|
|
272
394
|
let common = topology.common_ancestor(&left.id, &right.id);
|
|
273
395
|
assert_eq!(common, Some(root.id.clone()));
|
|
274
396
|
}
|
|
@@ -276,40 +398,70 @@ mod feltdb_state_boundary_tests {
|
|
|
276
398
|
#[test]
|
|
277
399
|
fn boundary_relationship_types() {
|
|
278
400
|
let store = FeltDBStateSystem::create_test_store();
|
|
279
|
-
|
|
280
|
-
let s1 = store
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
401
|
+
|
|
402
|
+
let s1 = store
|
|
403
|
+
.create("docs:18", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
404
|
+
.unwrap();
|
|
405
|
+
let s2 = store
|
|
406
|
+
.commit(r#"{"v":2}"#.to_string(), &s1, "auth".to_string())
|
|
407
|
+
.unwrap();
|
|
408
|
+
let s3 = store
|
|
409
|
+
.commit(r#"{"v":3}"#.to_string(), &s1, "auth".to_string())
|
|
410
|
+
.unwrap();
|
|
411
|
+
|
|
284
412
|
let mut topology = StateTopology::new();
|
|
285
413
|
topology.add_revision(s1.clone());
|
|
286
414
|
topology.add_revision(s2.clone());
|
|
287
415
|
topology.add_revision(s3.clone());
|
|
288
|
-
|
|
416
|
+
|
|
289
417
|
// Identity
|
|
290
|
-
assert!(matches!(
|
|
291
|
-
|
|
418
|
+
assert!(matches!(
|
|
419
|
+
topology.relationship(&s1.id, &s1.id),
|
|
420
|
+
Relationship::Identity
|
|
421
|
+
));
|
|
422
|
+
|
|
292
423
|
// Ancestor
|
|
293
|
-
assert!(matches!(
|
|
294
|
-
|
|
424
|
+
assert!(matches!(
|
|
425
|
+
topology.relationship(&s1.id, &s2.id),
|
|
426
|
+
Relationship::Ancestor
|
|
427
|
+
));
|
|
428
|
+
|
|
295
429
|
// Descendant
|
|
296
|
-
assert!(matches!(
|
|
297
|
-
|
|
430
|
+
assert!(matches!(
|
|
431
|
+
topology.relationship(&s2.id, &s1.id),
|
|
432
|
+
Relationship::Descendant
|
|
433
|
+
));
|
|
434
|
+
|
|
298
435
|
// Diverged
|
|
299
|
-
assert!(matches!(
|
|
436
|
+
assert!(matches!(
|
|
437
|
+
topology.relationship(&s2.id, &s3.id),
|
|
438
|
+
Relationship::Diverged
|
|
439
|
+
));
|
|
300
440
|
}
|
|
301
441
|
|
|
302
442
|
#[test]
|
|
303
443
|
fn boundary_unrelated_histories() {
|
|
304
444
|
let store = FeltDBStateSystem::create_test_store();
|
|
305
|
-
|
|
306
|
-
let hist1_root = store
|
|
307
|
-
|
|
308
|
-
|
|
445
|
+
|
|
446
|
+
let hist1_root = store
|
|
447
|
+
.create(
|
|
448
|
+
"docs:19",
|
|
449
|
+
r#"{"history":1}"#.to_string(),
|
|
450
|
+
"auth".to_string(),
|
|
451
|
+
)
|
|
452
|
+
.unwrap();
|
|
453
|
+
let hist2_root = store
|
|
454
|
+
.create(
|
|
455
|
+
"docs:20",
|
|
456
|
+
r#"{"history":2}"#.to_string(),
|
|
457
|
+
"auth".to_string(),
|
|
458
|
+
)
|
|
459
|
+
.unwrap();
|
|
460
|
+
|
|
309
461
|
let mut topology = StateTopology::new();
|
|
310
462
|
topology.add_revision(hist1_root.clone());
|
|
311
463
|
topology.add_revision(hist2_root.clone());
|
|
312
|
-
|
|
464
|
+
|
|
313
465
|
assert!(matches!(
|
|
314
466
|
topology.relationship(&hist1_root.id, &hist2_root.id),
|
|
315
467
|
Relationship::Unrelated
|
|
@@ -324,9 +476,9 @@ mod feltdb_state_boundary_tests {
|
|
|
324
476
|
fn boundary_diff_deterministic_paths() {
|
|
325
477
|
let base: serde_json::Value = json!({"a": 1, "b": 2});
|
|
326
478
|
let modified: serde_json::Value = json!({"a": 2, "b": 2});
|
|
327
|
-
|
|
479
|
+
|
|
328
480
|
let diff = SemanticDiff::compute(&base, &modified);
|
|
329
|
-
|
|
481
|
+
|
|
330
482
|
// Should have exactly one change
|
|
331
483
|
assert_eq!(diff.changes.len(), 1);
|
|
332
484
|
assert!(!diff.changes[0].path.is_empty());
|
|
@@ -347,9 +499,9 @@ mod feltdb_state_boundary_tests {
|
|
|
347
499
|
{"name": "charlie"}
|
|
348
500
|
]
|
|
349
501
|
});
|
|
350
|
-
|
|
502
|
+
|
|
351
503
|
let diff = SemanticDiff::compute(&base, &modified);
|
|
352
|
-
|
|
504
|
+
|
|
353
505
|
// Should detect array element addition
|
|
354
506
|
assert!(!diff.changes.is_empty());
|
|
355
507
|
}
|
|
@@ -358,9 +510,9 @@ mod feltdb_state_boundary_tests {
|
|
|
358
510
|
fn boundary_diff_added_removed_changed() {
|
|
359
511
|
let base: serde_json::Value = json!({"x": 1, "y": 2});
|
|
360
512
|
let modified: serde_json::Value = json!({"x": 10, "z": 3});
|
|
361
|
-
|
|
513
|
+
|
|
362
514
|
let diff = SemanticDiff::compute(&base, &modified);
|
|
363
|
-
|
|
515
|
+
|
|
364
516
|
// Should have changes for: x changed, y removed, z added
|
|
365
517
|
assert!(diff.changes.len() >= 2);
|
|
366
518
|
}
|
|
@@ -369,9 +521,9 @@ mod feltdb_state_boundary_tests {
|
|
|
369
521
|
fn boundary_diff_type_sensitive() {
|
|
370
522
|
let base: serde_json::Value = json!({"v": "1"});
|
|
371
523
|
let modified: serde_json::Value = json!({"v": 1});
|
|
372
|
-
|
|
524
|
+
|
|
373
525
|
let diff = SemanticDiff::compute(&base, &modified);
|
|
374
|
-
|
|
526
|
+
|
|
375
527
|
// Should detect type change
|
|
376
528
|
assert!(!diff.changes.is_empty());
|
|
377
529
|
}
|
|
@@ -380,9 +532,9 @@ mod feltdb_state_boundary_tests {
|
|
|
380
532
|
fn boundary_diff_read_only() {
|
|
381
533
|
let base: serde_json::Value = json!({"original": true});
|
|
382
534
|
let modified: serde_json::Value = json!({"modified": true});
|
|
383
|
-
|
|
535
|
+
|
|
384
536
|
let _diff = SemanticDiff::compute(&base, &modified);
|
|
385
|
-
|
|
537
|
+
|
|
386
538
|
// Diff should not modify inputs
|
|
387
539
|
assert_eq!(base.get("original"), Some(&json!(true)));
|
|
388
540
|
assert_eq!(modified.get("modified"), Some(&json!(true)));
|
|
@@ -395,13 +547,23 @@ mod feltdb_state_boundary_tests {
|
|
|
395
547
|
#[test]
|
|
396
548
|
fn boundary_conflict_independent() {
|
|
397
549
|
let store = FeltDBStateSystem::create_test_store();
|
|
398
|
-
|
|
399
|
-
let base = store
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
550
|
+
|
|
551
|
+
let base = store
|
|
552
|
+
.create(
|
|
553
|
+
"docs:21",
|
|
554
|
+
r#"{"a":1,"b":1}"#.to_string(),
|
|
555
|
+
"auth".to_string(),
|
|
556
|
+
)
|
|
557
|
+
.unwrap();
|
|
558
|
+
let left = store
|
|
559
|
+
.commit(r#"{"a":2,"b":1}"#.to_string(), &base, "auth".to_string())
|
|
560
|
+
.unwrap();
|
|
561
|
+
let right = store
|
|
562
|
+
.commit(r#"{"a":1,"b":2}"#.to_string(), &base, "auth".to_string())
|
|
563
|
+
.unwrap();
|
|
564
|
+
|
|
403
565
|
let classification = ConflictClassification::classify(&base, &left, &right);
|
|
404
|
-
|
|
566
|
+
|
|
405
567
|
// Modifications to different fields are independent
|
|
406
568
|
assert_eq!(classification.overall, ConflictClass::Independent);
|
|
407
569
|
}
|
|
@@ -409,13 +571,19 @@ mod feltdb_state_boundary_tests {
|
|
|
409
571
|
#[test]
|
|
410
572
|
fn boundary_conflict_convergent() {
|
|
411
573
|
let store = FeltDBStateSystem::create_test_store();
|
|
412
|
-
|
|
413
|
-
let base = store
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
574
|
+
|
|
575
|
+
let base = store
|
|
576
|
+
.create("docs:22", r#"{"value":1}"#.to_string(), "auth".to_string())
|
|
577
|
+
.unwrap();
|
|
578
|
+
let left = store
|
|
579
|
+
.commit(r#"{"value":2}"#.to_string(), &base, "auth".to_string())
|
|
580
|
+
.unwrap();
|
|
581
|
+
let right = store
|
|
582
|
+
.commit(r#"{"value":2}"#.to_string(), &base, "auth".to_string())
|
|
583
|
+
.unwrap();
|
|
584
|
+
|
|
417
585
|
let classification = ConflictClassification::classify(&base, &left, &right);
|
|
418
|
-
|
|
586
|
+
|
|
419
587
|
// Both made same change → convergent
|
|
420
588
|
assert_eq!(classification.overall, ConflictClass::Convergent);
|
|
421
589
|
}
|
|
@@ -423,13 +591,23 @@ mod feltdb_state_boundary_tests {
|
|
|
423
591
|
#[test]
|
|
424
592
|
fn boundary_conflict_actual_conflict() {
|
|
425
593
|
let store = FeltDBStateSystem::create_test_store();
|
|
426
|
-
|
|
427
|
-
let base = store
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
594
|
+
|
|
595
|
+
let base = store
|
|
596
|
+
.create(
|
|
597
|
+
"docs:23",
|
|
598
|
+
r#"{"balance":100}"#.to_string(),
|
|
599
|
+
"auth".to_string(),
|
|
600
|
+
)
|
|
601
|
+
.unwrap();
|
|
602
|
+
let left = store
|
|
603
|
+
.commit(r#"{"balance":150}"#.to_string(), &base, "auth".to_string())
|
|
604
|
+
.unwrap();
|
|
605
|
+
let right = store
|
|
606
|
+
.commit(r#"{"balance":120}"#.to_string(), &base, "auth".to_string())
|
|
607
|
+
.unwrap();
|
|
608
|
+
|
|
431
609
|
let classification = ConflictClassification::classify(&base, &left, &right);
|
|
432
|
-
|
|
610
|
+
|
|
433
611
|
// Different modifications to same field → conflict
|
|
434
612
|
assert_eq!(classification.overall, ConflictClass::Conflict);
|
|
435
613
|
}
|
|
@@ -437,13 +615,19 @@ mod feltdb_state_boundary_tests {
|
|
|
437
615
|
#[test]
|
|
438
616
|
fn boundary_conflict_authority_neutral() {
|
|
439
617
|
let store = FeltDBStateSystem::create_test_store();
|
|
440
|
-
|
|
441
|
-
let base = store
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
618
|
+
|
|
619
|
+
let base = store
|
|
620
|
+
.create("docs:24", r#"{"v":1}"#.to_string(), "alice".to_string())
|
|
621
|
+
.unwrap();
|
|
622
|
+
let left = store
|
|
623
|
+
.commit(r#"{"v":2}"#.to_string(), &base, "alice".to_string())
|
|
624
|
+
.unwrap();
|
|
625
|
+
let right = store
|
|
626
|
+
.commit(r#"{"v":2}"#.to_string(), &base, "bob".to_string())
|
|
627
|
+
.unwrap();
|
|
628
|
+
|
|
445
629
|
let classification = ConflictClassification::classify(&base, &left, &right);
|
|
446
|
-
|
|
630
|
+
|
|
447
631
|
// Classification doesn't prefer one authority over another
|
|
448
632
|
// It should classify based on content, not authority
|
|
449
633
|
assert_eq!(classification.overall, ConflictClass::Convergent);
|
|
@@ -456,11 +640,17 @@ mod feltdb_state_boundary_tests {
|
|
|
456
640
|
#[test]
|
|
457
641
|
fn boundary_authority_recorded() {
|
|
458
642
|
let store = FeltDBStateSystem::create_test_store();
|
|
459
|
-
|
|
460
|
-
let state = store
|
|
461
|
-
|
|
643
|
+
|
|
644
|
+
let state = store
|
|
645
|
+
.create(
|
|
646
|
+
"docs:25",
|
|
647
|
+
r#"{"data":"value"}"#.to_string(),
|
|
648
|
+
"my-authority".to_string(),
|
|
649
|
+
)
|
|
650
|
+
.unwrap();
|
|
651
|
+
|
|
462
652
|
assert_eq!(state.authority, "my-authority");
|
|
463
|
-
|
|
653
|
+
|
|
464
654
|
let retrieved = store.get(&state.id).unwrap();
|
|
465
655
|
assert_eq!(retrieved.authority, "my-authority");
|
|
466
656
|
}
|
|
@@ -468,12 +658,18 @@ mod feltdb_state_boundary_tests {
|
|
|
468
658
|
#[test]
|
|
469
659
|
fn boundary_authority_does_not_affect_id() {
|
|
470
660
|
let store = FeltDBStateSystem::create_test_store();
|
|
471
|
-
|
|
472
|
-
let state1 = store
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
661
|
+
|
|
662
|
+
let state1 = store
|
|
663
|
+
.create("docs:26", r#"{"x":1}"#.to_string(), "auth1".to_string())
|
|
664
|
+
.unwrap();
|
|
665
|
+
let state2 = store
|
|
666
|
+
.create("docs:27", r#"{"x":1}"#.to_string(), "auth2".to_string())
|
|
667
|
+
.unwrap();
|
|
668
|
+
|
|
669
|
+
// Authority takes no part in either identity. The content identity is
|
|
670
|
+
// equal; the revision identities differ only because the resources do.
|
|
671
|
+
assert_eq!(state1.content_id, state2.content_id);
|
|
672
|
+
assert_ne!(state1.id, state2.id);
|
|
477
673
|
}
|
|
478
674
|
|
|
479
675
|
// ========================================================================
|
|
@@ -483,19 +679,23 @@ mod feltdb_state_boundary_tests {
|
|
|
483
679
|
#[test]
|
|
484
680
|
fn boundary_topology_is_read_only() {
|
|
485
681
|
let store = FeltDBStateSystem::create_test_store();
|
|
486
|
-
|
|
487
|
-
let s1 = store
|
|
488
|
-
|
|
489
|
-
|
|
682
|
+
|
|
683
|
+
let s1 = store
|
|
684
|
+
.create("docs:28", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
685
|
+
.unwrap();
|
|
686
|
+
let s2 = store
|
|
687
|
+
.commit(r#"{"v":2}"#.to_string(), &s1, "auth".to_string())
|
|
688
|
+
.unwrap();
|
|
689
|
+
|
|
490
690
|
let mut topology = StateTopology::new();
|
|
491
691
|
topology.add_revision(s1.clone());
|
|
492
692
|
topology.add_revision(s2.clone());
|
|
493
|
-
|
|
693
|
+
|
|
494
694
|
// Topology operations shouldn't mutate states
|
|
495
695
|
let _rel = topology.relationship(&s1.id, &s2.id);
|
|
496
696
|
let _anc = topology.is_ancestor(&s1.id, &s2.id);
|
|
497
697
|
let _ancestors = topology.ancestors(&s2.id);
|
|
498
|
-
|
|
698
|
+
|
|
499
699
|
// States unchanged
|
|
500
700
|
assert_eq!(store.get(&s1.id).unwrap().id, s1.id);
|
|
501
701
|
assert_eq!(store.get(&s2.id).unwrap().id, s2.id);
|
|
@@ -505,9 +705,9 @@ mod feltdb_state_boundary_tests {
|
|
|
505
705
|
fn boundary_diff_is_read_only() {
|
|
506
706
|
let base: serde_json::Value = json!({"immutable": "base"});
|
|
507
707
|
let modified: serde_json::Value = json!({"immutable": "base", "changed": true});
|
|
508
|
-
|
|
708
|
+
|
|
509
709
|
let _diff = SemanticDiff::compute(&base, &modified);
|
|
510
|
-
|
|
710
|
+
|
|
511
711
|
// Input values unchanged
|
|
512
712
|
assert_eq!(base.get("immutable"), Some(&json!("base")));
|
|
513
713
|
assert_eq!(modified.get("immutable"), Some(&json!("base")));
|
|
@@ -516,13 +716,19 @@ mod feltdb_state_boundary_tests {
|
|
|
516
716
|
#[test]
|
|
517
717
|
fn boundary_classification_is_read_only() {
|
|
518
718
|
let store = FeltDBStateSystem::create_test_store();
|
|
519
|
-
|
|
520
|
-
let base = store
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
719
|
+
|
|
720
|
+
let base = store
|
|
721
|
+
.create("docs:29", r#"{"v":1}"#.to_string(), "auth".to_string())
|
|
722
|
+
.unwrap();
|
|
723
|
+
let left = store
|
|
724
|
+
.commit(r#"{"v":2}"#.to_string(), &base, "auth".to_string())
|
|
725
|
+
.unwrap();
|
|
726
|
+
let right = store
|
|
727
|
+
.commit(r#"{"v":3}"#.to_string(), &base, "auth".to_string())
|
|
728
|
+
.unwrap();
|
|
729
|
+
|
|
524
730
|
let _classification = ConflictClassification::classify(&base, &left, &right);
|
|
525
|
-
|
|
731
|
+
|
|
526
732
|
// States unchanged
|
|
527
733
|
assert_eq!(store.get(&base.id).unwrap().id, base.id);
|
|
528
734
|
assert_eq!(store.get(&left.id).unwrap().id, left.id);
|
|
@@ -537,26 +743,33 @@ mod feltdb_state_boundary_tests {
|
|
|
537
743
|
fn boundary_no_git_dependency() {
|
|
538
744
|
// This test should pass without any .git directory or Git runtime
|
|
539
745
|
let store = FeltDBStateSystem::create_test_store();
|
|
540
|
-
|
|
541
|
-
let state = store
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
746
|
+
|
|
747
|
+
let state = store
|
|
748
|
+
.create(
|
|
749
|
+
"docs:30",
|
|
750
|
+
r#"{"git-independent": true}"#.to_string(),
|
|
751
|
+
"auth".to_string(),
|
|
752
|
+
)
|
|
753
|
+
.expect("State operations should work without Git");
|
|
754
|
+
|
|
546
755
|
assert!(store.exists(&state.id));
|
|
547
756
|
}
|
|
548
757
|
|
|
549
758
|
#[test]
|
|
550
759
|
fn boundary_topology_no_git() {
|
|
551
760
|
let store = FeltDBStateSystem::create_test_store();
|
|
552
|
-
|
|
553
|
-
let s1 = store
|
|
554
|
-
|
|
555
|
-
|
|
761
|
+
|
|
762
|
+
let s1 = store
|
|
763
|
+
.create("docs:31", r#"{"seq":1}"#.to_string(), "auth".to_string())
|
|
764
|
+
.unwrap();
|
|
765
|
+
let s2 = store
|
|
766
|
+
.commit(r#"{"seq":2}"#.to_string(), &s1, "auth".to_string())
|
|
767
|
+
.unwrap();
|
|
768
|
+
|
|
556
769
|
let mut topology = StateTopology::new();
|
|
557
770
|
topology.add_revision(s1.clone());
|
|
558
771
|
topology.add_revision(s2.clone());
|
|
559
|
-
|
|
772
|
+
|
|
560
773
|
// All topology operations should work without Git
|
|
561
774
|
let _ancestors = topology.ancestors(&s2.id);
|
|
562
775
|
let _is_anc = topology.is_ancestor(&s1.id, &s2.id);
|
|
@@ -574,58 +787,61 @@ mod feltdb_state_boundary_tests {
|
|
|
574
787
|
|
|
575
788
|
// 1. Initialize
|
|
576
789
|
let store = FeltDBStateSystem::create_test_store();
|
|
577
|
-
|
|
790
|
+
|
|
578
791
|
// 2. Create initial application state
|
|
579
|
-
let initial = store
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
792
|
+
let initial = store
|
|
793
|
+
.create(
|
|
794
|
+
"docs:32",
|
|
795
|
+
r#"{"users":{"alice":{"balance":100}}}"#.to_string(),
|
|
796
|
+
"app-authority".to_string(),
|
|
797
|
+
)
|
|
798
|
+
.expect("Create initial state");
|
|
799
|
+
|
|
584
800
|
// 3. Make first update
|
|
585
|
-
let update1 = store
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
801
|
+
let update1 = store
|
|
802
|
+
.commit(
|
|
803
|
+
r#"{"users":{"alice":{"balance":150}}}"#.to_string(),
|
|
804
|
+
&initial,
|
|
805
|
+
"alice".to_string(),
|
|
806
|
+
)
|
|
807
|
+
.expect("First update");
|
|
808
|
+
|
|
591
809
|
// 4. Branch: parallel update from initial
|
|
592
|
-
let update2 = store
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
810
|
+
let update2 = store
|
|
811
|
+
.commit(
|
|
812
|
+
r#"{"users":{"alice":{"balance":100},"bob":{"balance":50}}}"#.to_string(),
|
|
813
|
+
&initial,
|
|
814
|
+
"bob".to_string(),
|
|
815
|
+
)
|
|
816
|
+
.expect("Parallel update");
|
|
817
|
+
|
|
600
818
|
// 5. Analyze topology
|
|
601
819
|
let mut topology = StateTopology::new();
|
|
602
820
|
topology.add_revision(initial.clone());
|
|
603
821
|
topology.add_revision(update1.clone());
|
|
604
822
|
topology.add_revision(update2.clone());
|
|
605
|
-
|
|
823
|
+
|
|
606
824
|
assert!(matches!(
|
|
607
825
|
topology.relationship(&update1.id, &update2.id),
|
|
608
826
|
Relationship::Diverged
|
|
609
827
|
));
|
|
610
|
-
|
|
828
|
+
|
|
611
829
|
// 6. Compute diffs
|
|
612
|
-
let initial_json: serde_json::Value =
|
|
613
|
-
r#"{"users":{"alice":{"balance":100}}}"#
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
).unwrap();
|
|
618
|
-
|
|
830
|
+
let initial_json: serde_json::Value =
|
|
831
|
+
serde_json::from_str(r#"{"users":{"alice":{"balance":100}}}"#).unwrap();
|
|
832
|
+
let update1_json: serde_json::Value =
|
|
833
|
+
serde_json::from_str(r#"{"users":{"alice":{"balance":150}}}"#).unwrap();
|
|
834
|
+
|
|
619
835
|
let diff = SemanticDiff::compute(&initial_json, &update1_json);
|
|
620
836
|
assert!(!diff.changes.is_empty());
|
|
621
|
-
|
|
837
|
+
|
|
622
838
|
// 7. Classify conflict
|
|
623
839
|
let classification = ConflictClassification::classify(&initial, &update1, &update2);
|
|
624
840
|
assert_eq!(classification.overall, ConflictClass::Independent);
|
|
625
|
-
|
|
841
|
+
|
|
626
842
|
// 8. Verify current pointer
|
|
627
|
-
assert_eq!(store.
|
|
628
|
-
|
|
843
|
+
assert_eq!(store.get(&update2.id).unwrap().id, update2.id);
|
|
844
|
+
|
|
629
845
|
// 9. Verify all states exist
|
|
630
846
|
assert!(store.exists(&initial.id));
|
|
631
847
|
assert!(store.exists(&update1.id));
|