Skip to content

Implement key management methods in cliSession with enhanced test coverage - #33

Merged
evgenyk merged 3 commits into
mainfrom
ev/error_handling
Sep 7, 2025
Merged

Implement key management methods in cliSession with enhanced test coverage#33
evgenyk merged 3 commits into
mainfrom
ev/error_handling

Conversation

@evgenyk

@evgenyk evgenyk commented Sep 6, 2025

Copy link
Copy Markdown
Contributor

Introduce methods for setting, getting, and deleting keys in cliSession, along with improved test coverage for these operations. This update enhances the key management functionality and ensures robustness through comprehensive testing.

@coderabbitai

coderabbitai Bot commented Sep 6, 2025

Copy link
Copy Markdown

Walkthrough

Adds a public ICliSession interface and implements SetKey/GetKey/DeleteKey with chunked key/value storage and legacy fallback; refactors GetRawToken/SetRawToken to use those methods; updates NewCliSession return type; expands CLI session tests and propagates SetRawToken errors from ExchangeCode.

Changes

Cohort / File(s) Summary
CLI session interface & storage
frameworks/cli/cliSession.go
Adds exported ICliSession (extends authorization_code.ISessionHooks) and implements SetKey, GetKey, DeleteKey with 1KB chunking, legacy fallback, chunk_count management, and error aggregation; refactors GetRawToken/SetRawToken to delegate to these methods; changes NewCliSession return type to ICliSession; imports updated for error handling.
CLI session tests
frameworks/cli/cliSession_test.go
Extends mockKeyring with RemoveFunc hook to simulate removal errors; adds tests covering DeleteKey/GetKey single-key and chunked scenarios, missing chunks, chunk_count parse errors, and no-op deletions.
OAuth2 authorization flow
oauth2/authorization_code/authorization_code.go
ExchangeCode now captures and returns the error from flow.sessionHooks.SetRawToken(token) instead of ignoring it.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI Client
  participant Sess as CliSession (ICliSession)
  participant KR as Keyring

  rect rgb(240,248,255)
    Note over CLI,Sess: Store token (SetRawToken)
    CLI->>Sess: SetRawToken(token)
    alt token == nil
      Sess->>Sess: DeleteKey(baseKey)
      Sess->>KR: Remove(baseKey, chunk_*, chunk_count)
      KR-->>Sess: ok / not found / error
    else token != nil
      Sess->>Sess: Marshal(token) -> SetKey(baseKey, bytes)
      Sess->>KR: Remove(existing chunks)
      Sess->>KR: Set(baseKey_chunk_0..N) + Set(chunk_count)
      KR-->>Sess: ok / error
    end
    Sess-->>CLI: success / error
  end
Loading
sequenceDiagram
  participant CLI as CLI Client
  participant Sess as CliSession (ICliSession)
  participant KR as Keyring

  rect rgb(245,255,250)
    Note over CLI,Sess: Retrieve token (GetRawToken)
    CLI->>Sess: GetRawToken()
    Sess->>Sess: GetKey(baseKey)
    alt chunk_count exists
      Sess->>KR: Get(chunk_0..N)
      KR-->>Sess: bytes / error
      Sess->>Sess: concat bytes -> unmarshal token
    else legacy single key
      Sess->>KR: Get(baseKey)
      KR-->>Sess: bytes / not found / error
      Sess->>Sess: unmarshal / return not found
    end
    Sess-->>CLI: token / error
  end
Loading
sequenceDiagram
  participant Client as OAuth2 Client
  participant Flow as ExchangeCode
  participant Server as Auth Server
  participant Sess as sessionHooks

  Client->>Flow: ExchangeCode(authCode)
  Flow->>Server: token request
  Server-->>Flow: token / error
  alt token received
    Flow->>Sess: SetRawToken(token)
    Sess-->>Flow: ok / error
    alt SetRawToken error
      Flow-->>Client: return error
    else
      Flow-->>Client: success
    end
  else error
    Flow-->>Client: return error
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ev/error_handling

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@evgenyk
evgenyk changed the base branch from ev/fixed_default_cli_opts to main September 6, 2025 13:06
@evgenyk

evgenyk commented Sep 6, 2025

Copy link
Copy Markdown
Contributor Author

@CodeRabbit

@coderabbitai

coderabbitai Bot commented Sep 6, 2025

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@evgenyk Hello! I see you've mentioned me. How can I help you with this PR? I'm ready to assist with code review, analysis, or any questions you might have about the key management methods implementation and test coverage.

@evgenyk

evgenyk commented Sep 6, 2025

Copy link
Copy Markdown
Contributor Author

@CodeRabbit code review please

@coderabbitai

coderabbitai Bot commented Sep 6, 2025

Copy link
Copy Markdown

@evgenyk I'll review the PR changes for you right now, focusing on the key management methods implementation in cliSession and the test coverage enhancements.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frameworks/cli/cliSession.go (1)

168-213: SetKey implementation needs attention to error handling and cleanup

There are several issues with the current implementation:

  1. Variable shadowing (Line 180): The chunks variable from Line 177 is shadowed by the loop variable, which could cause confusion.

  2. Silent failure on chunk removal (Line 184-186): Similar to DeleteKey, breaking on error leaves partial chunks in place without proper error reporting.

  3. Duplicate countKey assignment (Line 204): The countKey variable is reassigned with the same value it already has from Line 169.

Here's an improved implementation:

 func (c *cliSession) SetKey(key string, value []byte) error {
 	countKey := fmt.Sprintf("%s_chunk_count", key)
 
 	if len(value) == 0 {
 		// Remove legacy single-key entry
 		return c.DeleteKey(key)
 	}
 
 	const chunkSize = 1024
-	chunks := (len(value) + chunkSize - 1) / chunkSize
+	numChunks := (len(value) + chunkSize - 1) / chunkSize
 
 	// Try to get chunk count
-	if chunks, err := c.getChunkCount(countKey); err == nil {
+	if existingChunks, err := c.getChunkCount(countKey); err == nil {
 		// Remove any existing chunks
-		for i := range chunks {
+		for i := range existingChunks {
 			chunkKey := fmt.Sprintf("%s_chunk_%d", key, i)
 			if err := c.keyring.Remove(chunkKey); err != nil {
-				break
+				if !errors.Is(err, keyring.ErrKeyNotFound) {
+					// Best effort cleanup - continue removing other chunks
+					// but return error at the end
+				}
 			}
 		}
 	}
 
 	// Save chunks
-	for i := range chunks {
+	for i := range numChunks {
 		start := i * chunkSize
 		end := min(start+chunkSize, len(value))
 		chunkKey := fmt.Sprintf("%s_chunk_%d", key, i)
 		if err := c.keyring.Set(keyring.Item{
 			Key:  chunkKey,
 			Data: value[start:end],
 		}); err != nil {
 			return fmt.Errorf("failed to save token chunk %d: %w", i, err)
 		}
 	}
 
 	// Save chunk count
-	countKey = fmt.Sprintf("%s_chunk_count", key)
-	countData := fmt.Appendf(nil, "%d", chunks)
+	countData := fmt.Appendf(nil, "%d", numChunks)
 	if err := c.keyring.Set(keyring.Item{
 		Key:  countKey,
 		Data: countData,
 	}); err != nil {
 		return fmt.Errorf("failed to save chunk count: %w", err)
 	}
 	return nil
 }
🧹 Nitpick comments (2)
frameworks/cli/cliSession_test.go (1)

296-321: Consider enhancing the chunk removal error test

The test correctly verifies that chunk removal errors don't propagate (Line 316), but the comment on Line 320 indicates that chunk_1 is "not removed due to break". However, looking at the DeleteKey implementation in cliSession.go, the loop does break on error (Line 53), which would leave subsequent chunks intact.

Consider adding an assertion to verify that the chunk count key is also not removed when chunk deletion fails, as this could lead to inconsistent state.

 func TestCliSession_DeleteKey_ChunkRemoveError(t *testing.T) {
 	assert := assert.New(t)
 	mk := &mockKeyring{
 		items: map[string]keyring.Item{
 			"mykey_chunk_count": {Key: "mykey_chunk_count", Data: []byte("2")},
 			"mykey_chunk_0":     {Key: "mykey_chunk_0", Data: []byte("chunk1")},
 			"mykey_chunk_1":     {Key: "mykey_chunk_1", Data: []byte("chunk2")},
 		},
 		getErrs: map[string]error{},
 	}
 	// Simulate error on removing chunk_0
 	mk.RemoveFunc = func(key string) error {
 		if key == "mykey_chunk_0" {
 			return errors.New("remove error")
 		}
 		delete(mk.items, key)
 		return nil
 	}
 	cs := &cliSession{keyring: mk}
 	err := cs.DeleteKey("mykey")
 	assert.Nil(err) // Should not propagate error
 	_, exists0 := mk.items["mykey_chunk_0"]
 	_, exists1 := mk.items["mykey_chunk_1"]
+	_, existsCount := mk.items["mykey_chunk_count"]
 	assert.True(exists0) // chunk_0 not removed due to error
 	assert.True(exists1) // chunk_1 not removed due to break
+	assert.True(existsCount) // chunk_count should also remain due to early return
 }
frameworks/cli/cliSession.go (1)

160-166: Redundant nil return statement

The explicit return nil on Line 165 is redundant since err will already be nil if SetKey succeeds. Consider simplifying:

 	err = c.SetKey(key, t)
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return err
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1124e47 and 73e512a.

📒 Files selected for processing (3)
  • frameworks/cli/cliSession.go (6 hunks)
  • frameworks/cli/cliSession_test.go (3 hunks)
  • oauth2/authorization_code/authorization_code.go (1 hunks)
🔇 Additional comments (9)
oauth2/authorization_code/authorization_code.go (1)

261-263: Good fix for proper error propagation

The change correctly propagates the error from SetRawToken, ensuring that token storage failures are properly handled and reported to the caller. This is an important improvement for error handling.

frameworks/cli/cliSession_test.go (3)

27-30: LGTM! Flexible mock design for testing error paths

The addition of RemoveFunc to the mock allows for simulating removal errors in tests, which is essential for comprehensive error path coverage.


42-54: Well-structured mock methods with proper error injection support

The refactored mock methods maintain clear semantics while enabling controllable error simulation through RemoveFunc. The implementation correctly handles the delete operation when RemoveFunc is not set.


162-332: Excellent test coverage for key management operations

The test suite provides comprehensive coverage for:

  • Single and chunked key operations
  • Error handling scenarios (parse errors, missing chunks, removal failures)
  • Edge cases (non-existent keys, empty operations)
  • Legacy key cleanup during chunked operations

The tests are well-structured and thoroughly validate the chunked storage implementation.

frameworks/cli/cliSession.go (5)

26-31: Good interface design for extended key management

The ICliSession interface cleanly extends ISessionHooks with generic key management methods, providing a clear separation of concerns and improved API surface for CLI session operations.


67-92: LGTM! Clean implementation of GetKey with backwards compatibility

The method correctly handles both chunked and single-key storage formats, providing seamless backwards compatibility for existing data.


135-138: Improved error message clarity

The error message has been updated from "failed to get token" to "failed to read token", which better describes the operation being performed.


152-153: Good simplification using DeleteKey

Using DeleteKey for nil token handling ensures consistent cleanup of both single and chunked token data.


244-244: Good API evolution with backwards compatibility

The return type change from ISessionHooks to ICliSession maintains backwards compatibility while exposing the new key management functionality.

Comment thread frameworks/cli/cliSession.go
coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 6, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
frameworks/cli/cliSession_test.go (3)

49-54: Guard against nil map in Set and return actual keys from Keys

Makes the mock safer and more generally useful.

Apply this diff:

 func (m *mockKeyring) Set(item keyring.Item) error {
-	m.items[item.Key] = item
+	if m.items == nil {
+		m.items = map[string]keyring.Item{}
+	}
+	m.items[item.Key] = item
 	return nil
 }
-func (m *mockKeyring) Keys() ([]string, error) { return nil, nil }
+func (m *mockKeyring) Keys() ([]string, error) {
+	keys := make([]string, 0, len(m.items))
+	for k := range m.items {
+		keys = append(keys, k)
+	}
+	return keys, nil
+}
 func (m *mockKeyring) Reset() error            { m.items = map[string]keyring.Item{}; return nil }

208-221: Use “key” not “token” in GetKey error assertions

These tests target GetKey; aligning messages avoids confusion with token-specific flows.

Apply this diff:

-	assert.Contains(err.Error(), "failed to get token")
+	assert.Contains(err.Error(), "failed to get key")
-	assert.Contains(err.Error(), "failed to get token chunk 0")
+	assert.Contains(err.Error(), "failed to get key chunk 0")
-	assert.Contains(err.Error(), "failed to get token")
+	assert.Contains(err.Error(), "failed to get key")

If the implementation intentionally reuses token-oriented messages for generic keys, please confirm and we can keep the current assertions.

Also applies to: 223-238, 240-254


296-323: Consider asserting chunk-count key behavior on partial failure

When chunk_0 removal fails, decide whether chunk_count should be retained or also removed and assert accordingly to lock semantics. Optionally add a test for failure removing the chunk_count key itself.

Do you want me to draft a companion test (e.g., DeleteKey_ChunkCountRemoveError) that simulates an error on removing "mykey_chunk_count"?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 73e512a and e94191e.

📒 Files selected for processing (2)
  • frameworks/cli/cliSession.go (6 hunks)
  • frameworks/cli/cliSession_test.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frameworks/cli/cliSession.go
🔇 Additional comments (7)
frameworks/cli/cliSession_test.go (7)

27-30: Mock extension for removal error injection looks good

Adding RemoveFunc provides precise control in tests without complicating the happy path.


42-48: Remove delegation + no-op on missing keys — LGTM

Behavior is predictable and fits the tests’ needs.


161-171: DeleteKey should be no-op when key is absent — correct

The test asserts the desired behavior.


172-187: Single-key retrieval path covered well

Happy-path GetKey without chunking is validated.


189-206: Chunked retrieval path covered well

Two-chunk reconstruction is exercised cleanly.


255-270: Single-key delete path validated

State check post-delete is clear.


272-294: Chunked delete path validated, including legacy fallback

Verifies all fragments are removed — nice coverage.

@evgenyk
evgenyk merged commit 1cda23c into main Sep 7, 2025
4 checks passed
@evgenyk
evgenyk deleted the ev/error_handling branch September 8, 2025 04:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant