Clarify state version checks
- SHA
167622834ef4ab183796f66578c5c545e7bf02a8- Parents
-
7e7b4b3 - Tree
750210a
1676228
167622834ef4ab183796f66578c5c545e7bf02a87e7b4b3
750210a| Status | File | + | - |
|---|---|---|---|
| M |
README.md
|
3 | 0 |
| M |
src/fgof_state.f90
|
2 | 0 |
| M |
src/fgof_state_types.f90
|
1 | 0 |
| M |
test/test_scaffold.f90
|
2 | 0 |
| M |
test/test_state_io.f90
|
3 | 0 |
| M |
test/test_state_io_edges.f90
|
2 | 0 |
README.mdmodified@@ -35,6 +35,7 @@ Tracked today: | ||
| 35 | 35 | - `fgof-temp`-backed atomic replace semantics for saves |
| 36 | 36 | - side-effect-free reads and removes when state roots are missing |
| 37 | 37 | - version-aware state envelopes with explicit mismatch handling |
| 38 | +- explicit `version_checked` / `version_matched` result semantics for version-aware loads | |
| 38 | 39 | - malformed or unsupported state payloads rejected as version errors |
| 39 | 40 | - invalid version arguments rejected before filesystem writes |
| 40 | 41 | - tracked round-trip and version-mismatch examples |
@@ -99,7 +100,9 @@ Current semantics: | ||
| 99 | 100 | - `remove_state_document()` removes stored state files without creating missing roots as a side effect |
| 100 | 101 | - `save_state_text()` stores a small internal format header plus a positive document version; the default version is `1` |
| 101 | 102 | - `load_state_text()` surfaces the stored version on `state_text_result%document%version` |
| 103 | +- `state_text_result%version_checked` tells callers whether `expected_version=` was actually applied for that load | |
| 102 | 104 | - `load_state_text(..., expected_version=...)` reports `version` errors explicitly when the stored document version does not match the caller expectation |
| 105 | +- `state_text_result%version_matched` is only meaningful when `state_text_result%version_checked` is `.true.` | |
| 103 | 106 | - unsupported or malformed state payloads also report `version` errors instead of pretending to be valid text |
| 104 | 107 | - missing documents report `not-found` for load or remove flows |
| 105 | 108 | - document names, namespaces, and scopes must not be empty, contain `/`, or be `.` / `..` |
src/fgof_state.f90modified@@ -77,6 +77,7 @@ contains | ||
| 77 | 77 | type(state_text_result) :: result_value |
| 78 | 78 | |
| 79 | 79 | result_value%found = .false. |
| 80 | + result_value%version_checked = .false. | |
| 80 | 81 | result_value%version_matched = .true. |
| 81 | 82 | result_value%expected_version = 0 |
| 82 | 83 | result_value%error_code = FGOF_STATE_OK |
@@ -252,6 +253,7 @@ contains | ||
| 252 | 253 | result_value%error_message = "expected_version must be positive" |
| 253 | 254 | return |
| 254 | 255 | end if |
| 256 | + result_value%version_checked = local_expected_version > 0 | |
| 255 | 257 | |
| 256 | 258 | document = resolve_read_document(name, options) |
| 257 | 259 | result_value%document = document |
src/fgof_state_types.f90modified@@ -36,6 +36,7 @@ module fgof_state_types | ||
| 36 | 36 | |
| 37 | 37 | type, public :: state_text_result |
| 38 | 38 | logical :: found = .false. |
| 39 | + logical :: version_checked = .false. | |
| 39 | 40 | logical :: version_matched = .true. |
| 40 | 41 | integer :: expected_version = 0 |
| 41 | 42 | integer :: error_code = FGOF_STATE_OK |
test/test_scaffold.f90modified@@ -52,6 +52,7 @@ program test_scaffold | ||
| 52 | 52 | |
| 53 | 53 | text_result = clear_state_text_result() |
| 54 | 54 | if (text_result%found) error stop "state text result should start not found" |
| 55 | + if (text_result%version_checked) error stop "state text result should start with no version check applied" | |
| 55 | 56 | if (.not. text_result%version_matched) error stop "state text result should start version-matched" |
| 56 | 57 | if (text_result%expected_version /= 0) error stop "state text result should start with no expected version" |
| 57 | 58 | if (text_result%error_code /= FGOF_STATE_OK) error stop "state text result should start ok" |
@@ -87,6 +88,7 @@ program test_scaffold | ||
| 87 | 88 | |
| 88 | 89 | text_result = load_state_text("scaffold.txt") |
| 89 | 90 | if (.not. allocated(text_result%document%path)) error stop "load_state_text should return document metadata" |
| 91 | + if (text_result%version_checked) error stop "plain load_state_text should not report a version check" | |
| 90 | 92 | document = save_state_text("scaffold.txt", "hello") |
| 91 | 93 | if (document%error_code /= FGOF_STATE_OK) error stop "save_state_text should succeed for basic usage" |
| 92 | 94 | if (document%version /= 1) error stop "default save_state_text version should be one" |
test/test_state_io.f90modified@@ -29,15 +29,18 @@ program test_state_io | ||
| 29 | 29 | load_result = load_state_text("settings.json", options) |
| 30 | 30 | if (load_result%error_code /= FGOF_STATE_OK) error stop "load_state_text should succeed for saved documents" |
| 31 | 31 | if (.not. load_result%found) error stop "load_state_text should mark saved documents as found" |
| 32 | + if (load_result%version_checked) error stop "load_state_text without expected_version should not report a version check" | |
| 32 | 33 | if (load_result%text /= "hello world") error stop "load_state_text should return the saved text" |
| 33 | 34 | if (load_result%document%version /= 3) error stop "load_state_text should return the stored version" |
| 34 | 35 | |
| 35 | 36 | load_result = load_state_text("settings.json", options, expected_version=3) |
| 36 | 37 | if (load_result%error_code /= FGOF_STATE_OK) error stop "matching expected_version should succeed" |
| 38 | + if (.not. load_result%version_checked) error stop "matching expected_version should report that a version check happened" | |
| 37 | 39 | if (.not. load_result%version_matched) error stop "matching expected_version should report a version match" |
| 38 | 40 | |
| 39 | 41 | load_result = load_state_text("settings.json", options, expected_version=2) |
| 40 | 42 | if (load_result%error_code /= FGOF_STATE_ERR_VERSION) error stop "mismatched expected_version should report version error" |
| 43 | + if (.not. load_result%version_checked) error stop "mismatched expected_version should still report that a version check happened" | |
| 41 | 44 | if (load_result%version_matched) error stop "mismatched expected_version should report version mismatch" |
| 42 | 45 | if (load_result%document%version /= 3) error stop "version mismatch should still surface the stored version" |
| 43 | 46 | if (load_result%text /= "") error stop "version mismatch should not surface payload text yet" |
test/test_state_io_edges.f90modified@@ -3,6 +3,7 @@ program test_state_io_edges | ||
| 3 | 3 | FGOF_STATE_ERR_INVALID_OPTIONS, & |
| 4 | 4 | FGOF_STATE_ERR_IO, & |
| 5 | 5 | FGOF_STATE_ERR_NOT_FOUND, & |
| 6 | + FGOF_STATE_OK, & | |
| 6 | 7 | FGOF_STATE_ERR_VERSION, & |
| 7 | 8 | clear_state_options, & |
| 8 | 9 | load_state_text, & |
@@ -71,6 +72,7 @@ program test_state_io_edges | ||
| 71 | 72 | |
| 72 | 73 | load_result = load_state_text("state.json", options, expected_version=0) |
| 73 | 74 | if (load_result%error_code /= FGOF_STATE_ERR_INVALID_OPTIONS) error stop "non-positive expected versions should be rejected" |
| 75 | + if (load_result%version_checked) error stop "invalid expected versions should not report a completed version check" | |
| 74 | 76 | |
| 75 | 77 | contains |
| 76 | 78 | |