fix: propagate credential store errors instead of erasing them into false/absent - #138
Conversation
04a0bd2 to
8b3a455
Compare
|
One observation from writing the tests, deliberately not changed in this PR because it is a separate decision. The async accessors declare Both return The test normalises with Unrelated, but noticed while checking: |
|
@acoliver hi thanks for your contributing, please rebase the latest branch |
The crate is built as a cdylib whose napi_* symbols are supplied by the host Node process at load time. A `cargo test` binary has no such host, so linking fails with undefined napi_* symbols and unit tests cannot run at all. Deferring those symbols to load time lets the test harness link, without affecting the cdylib itself (napi_build already passes the same flag for the library target, and the release build is unchanged).
Both getSecret and deleteCredential collapsed every backend failure through .ok()/.is_ok(), reusing the sentinel that also means 'absent'. A locked or inaccessible store made a delete look like a successful removal while the secret stayed readable, and a failed read looked like a cache miss. The two behavior halves now share one rule via a single helper pair: - into_optional maps Ok to Some and NoEntry to None, and propagates every other read error, so a null result means the credential is absent. - into_deleted maps Ok to true and NoEntry to false, and propagates every other delete error, so false always means 'there was nothing to delete'. Public JS signatures are unchanged: getPassword stays (something) or null, getSecret stays null instead of erasing failures, and deletePassword is a deleteCredential alias with the same boolean. Covered under cargo test. The unit suite already carries upstream's password error-preservation tests.
8b3a455 to
2775381
Compare
|
Rebased onto current I also updated the PR to account for merged #136 and The new CI run is currently |
|
@codex review |
Summary
This PR preserves
NoEntryas the benign absence case while allowing every other provider error to reach JavaScript:getSecret()returns absence only forkeyring_core::Error::NoEntry; other failures throw or reject.deleteCredential()anddeletePassword()return or resolvetrueafter a successful delete andfalseonly forNoEntry; other failures throw or reject.deletePassword()declaration is corrected fromPromise<unknown>toPromise<boolean>.Fixes #137.
Why
The previous
.ok()and.is_ok()conversions discarded provider errors at the N-API boundary. A locked or inaccessible credential store could look like a missing secret. A failed delete could look like a credential that was already absent, leaving callers unable to determine whether the secret remained stored.keyring-corealready distinguishesNoEntryfromNoStorageAccess,Ambiguous, encoding failures, and other provider errors. This change preserves that distinction through the JavaScript API.Behavior
getSecret()getSecret()NoEntrygetSecret()Ok(())trueNoEntryfalseThe same mapping applies to
EntryandAsyncEntry, including thedeletePassword()aliases.Relationship to #136
PR #136 is now merged and owns the
getPassword()error-propagation behavior. This branch is rebased onto that work. The generic optional-result helper preserves the merged behavior while serving both password and secret reads. This PR does not duplicate #136's JavaScript failure tests.macOS provider status
The current Cargo resolution selects
apple-native-keyring-store1.0.2 through the existing compatible manifest range. Version 1.0.2 changed macOS deletion to use the checkedItemSearchOptions::delete()path, so a refused delete reaches this binding as an error. No dependency declaration or lockfile change is included here.See apple-native-keyring-store#22.
Compatibility
Existing success and absence results remain unchanged:
true.false.Provider failures that previously appeared as absence or
falsenow throw or reject. The generated declarations retain the existing common return types. The asyncdeletePassword()alias now accurately declaresPromise<boolean>instead ofPromise<unknown>.Tests
Rust unit tests cover the mapping boundary for:
NoEntryNoStorageAccessAmbiguousNative JavaScript integration tests use the real platform store and cover:
deleteCredential()returningtruefor an existing credential andfalseafter it is absentdeletePassword()alias parityNo mocks are used.
Local verification on macOS arm64:
Commit structure
The first commit adds a macOS-scoped linker setting needed for
cargo test. N-API symbols are supplied by Node when the addon loads, but the standalone Rust test binary otherwise fails to link them. The release native build and JavaScript integration suite pass with this setting.The second commit contains the result mapping, generated declaration, documentation, and behavioral tests.