Skip to content

Create unit tests for storage implementations #6

@koistya

Description

@koistya

Description

Both storage implementations (inMemoryStore and fileStore) lack unit tests. These are critical components that handle OAuth token persistence and need comprehensive test coverage.

What needs to be done

Create unit tests for both storage implementations covering:

For inMemoryStore:

  • All CRUD operations (get, set, delete, clear)
  • Verify isolation between different keys
  • Test null/undefined handling
  • Verify memory is actually cleared

For fileStore:

  • All CRUD operations with file system
  • Missing file scenarios (first run)
  • Invalid JSON in existing file
  • File permission errors
  • Concurrent access scenarios
  • Directory creation
  • File path edge cases

Why this matters

Token storage is security-critical:

  • Lost tokens force users to re-authenticate
  • Corrupted storage could expose partial tokens
  • Race conditions could mix tokens between users
  • Storage bugs could leak tokens in logs or temp files

Implementation considerations

⚠️ Note: This feature requires critical thinking during implementation. Consider:

  1. Test isolation: How do we ensure tests don't interfere with each other's file system state? Should we use temp directories?

  2. Mock vs Integration: Should we mock the file system or test against real files? Both approaches have merits.

  3. Alternative approach: Consider property-based testing (e.g., fast-check) to find edge cases automatically

  4. Performance testing: Should we test performance characteristics? (e.g., how many tokens can be stored before slowdown)

  5. Security testing: Should we verify tokens are not logged or exposed in error messages?

Test structure suggestion

// src/storage/memory.test.ts
import { expect, test, describe, beforeEach } from "bun:test";
import { inMemoryStore } from "./memory";

describe("inMemoryStore", () => {
  let store: TokenStore;
  
  beforeEach(() => {
    store = inMemoryStore();
  });
  
  test("should return null for non-existent keys", async () => {
    const result = await store.get("non-existent");
    expect(result).toBeNull();
  });
  
  test("should store and retrieve tokens", async () => {
    const tokens = {
      access_token: "access123",
      refresh_token: "refresh456",
      expires_at: new Date(Date.now() + 3600000).toISOString()
    };
    
    await store.set("test-key", tokens);
    const retrieved = await store.get("test-key");
    expect(retrieved).toEqual(tokens);
  });
  
  test("should handle concurrent operations", async () => {
    // Test race conditions...
  });
});

// src/storage/file.test.ts
import { expect, test, describe, beforeEach, afterEach } from "bun:test";
import { fileStore } from "./file";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

describe("fileStore", () => {
  let tempDir: string;
  let store: TokenStore;
  
  beforeEach(async () => {
    tempDir = await mkdtemp(join(tmpdir(), "oauth-test-"));
    store = fileStore(join(tempDir, "tokens.json"));
  });
  
  afterEach(async () => {
    await rm(tempDir, { recursive: true, force: true });
  });
  
  // ... tests
});

Edge cases to test

  • Unicode characters in keys/values
  • Very large tokens
  • Deeply nested token objects
  • Circular references (should these be handled?)
  • Storage quota exceeded
  • File system full
  • Read-only file system
  • Symbolic links
  • Network file systems

Skills required

  • TypeScript
  • Testing with Bun
  • File system operations
  • Async/await patterns
  • Test isolation techniques

Difficulty

Easy - Good for learning the codebase and testing practices

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions