{"id":35429864,"url":"https://github.com/sudokar/aws-sdk-vitest-mock","last_synced_at":"2026-04-01T19:37:00.608Z","repository":{"id":328237866,"uuid":"1114751007","full_name":"sudokar/aws-sdk-vitest-mock","owner":"sudokar","description":"🪶 A lightweight, 🟦 TypeScript-first mocking library for ☁️ AWS SDK v3 and ⚡ Vitest with 🚫 zero dependencies, 🔁 full ESM/CJS support, 🔗 chainable stubs, 🎯 partial/strict request matching, 📜 sequential responses, and 🧪 custom Vitest matchers — ideal for unit testing AWS SDK clients.","archived":false,"fork":false,"pushed_at":"2026-03-24T06:39:39.000Z","size":1761,"stargazers_count":16,"open_issues_count":3,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-25T08:11:27.644Z","etag":null,"topics":["aws-sdk","aws-sdk-v3","cdk","lambda","mocks","serverless","testing","typescript","unit-testing","vitest"],"latest_commit_sha":null,"homepage":"https://sudokar.github.io/aws-sdk-vitest-mock/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sudokar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"sudokar","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2025-12-11T20:43:45.000Z","updated_at":"2026-03-24T06:39:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sudokar/aws-sdk-vitest-mock","commit_stats":null,"previous_names":["sudokar/aws-sdk-vitest-mock"],"tags_count":107,"template":false,"template_full_name":null,"purl":"pkg:github/sudokar/aws-sdk-vitest-mock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sudokar%2Faws-sdk-vitest-mock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sudokar%2Faws-sdk-vitest-mock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sudokar%2Faws-sdk-vitest-mock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sudokar%2Faws-sdk-vitest-mock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sudokar","download_url":"https://codeload.github.com/sudokar/aws-sdk-vitest-mock/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sudokar%2Faws-sdk-vitest-mock/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291156,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-sdk","aws-sdk-v3","cdk","lambda","mocks","serverless","testing","typescript","unit-testing","vitest"],"created_at":"2026-01-02T20:03:14.960Z","updated_at":"2026-04-01T19:37:00.579Z","avatar_url":"https://github.com/sudokar.png","language":"TypeScript","funding_links":["https://github.com/sponsors/sudokar"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"logo.png\" alt=\"aws-sdk-vitest-mock logo\" width=\"180\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eAWS SDK Vitest Mock\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  A powerful, type-safe mocking library for AWS SDK v3 with Vitest\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/aws-sdk-vitest-mock\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/aws-sdk-vitest-mock?color=cb3837\u0026logo=npm\" alt=\"npm version\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://app.codacy.com/gh/sudokar/aws-sdk-vitest-mock/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_grade\"\u003e\n    \u003cimg src=\"https://app.codacy.com/project/badge/Grade/9f7e00cbb11c41f78cac6b85c8840672\" alt=\"Codacy Badge\" /\u003e\n  \u003c/a\u003e\n  \u003cimg alt=\"NPM Downloads\" src=\"https://img.shields.io/npm/dm/aws-sdk-vitest-mock\"\u003e\n  \u003cimg alt=\"GitHub Issues or Pull Requests\" src=\"https://img.shields.io/github/issues/sudokar/aws-sdk-vitest-mock\"\u003e\n  \u003ca href=\"https://github.com/sudokar/aws-sdk-vitest-mock/actions\"\u003e\n      \u003cimg src=\"https://github.com/sudokar/aws-sdk-vitest-mock/actions/workflows/ci.yml/badge.svg\" alt=\"CI Status\" /\u003e\n  \u003c/a\u003e\n  \u003cbr /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/ESM%20Support-yes-4B32C3?logo=typescript\" alt=\"ESM Support\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Zero%20Dependencies-yes-brightgreen\" alt=\"Zero Dependencies\" /\u003e\n  \u003ca href=\"https://eslint.org/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/code%20style-eslint-4B32C3?logo=eslint\" alt=\"ESLint\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://prettier.io/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/code%20style-prettier-F7B93E?logo=prettier\" alt=\"Prettier\" /\u003e\n  \u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Maintained-yes-brightgreen\" alt=\"Maintained: Yes\" /\u003e\n\u003c/p\u003e\n\n---\n\n## ✨ Features\n\n- 🎯 **Type-Safe Mocking** - Full TypeScript support with strict type checking\n- 📦 **Zero Dependencies** - No extra dependencies\n- 🔄 **Dual Module Support** - Works with both ESM and CommonJS\n- 🎭 **Flexible Mocking** - Support for partial matching, strict matching, and custom handlers\n- 🧩 **Chainable API** - Fluent interface for configuring multiple mock behaviors\n- 🔍 **Custom Matchers** - Vitest matchers for asserting AWS SDK command calls\n- 📚 **Comprehensive API Docs** – [Read the full documentation here](https://sudokar.github.io/aws-sdk-vitest-mock/)\n\n## 📦 Installation\n\n```bash\nbun add -D aws-sdk-vitest-mock\n```\n\nOr with other package managers:\n\n```bash\nnpm install --save-dev aws-sdk-vitest-mock\n```\n\n```bash\nyarn add -D aws-sdk-vitest-mock\n```\n\n```bash\npnpm add -D aws-sdk-vitest-mock\n```\n\n## ✅ Compatibility\n\n- Vitest: ^3 or ^4\n- AWS SDK: v3\n\n## 🚀 Quick Start\n\n### Basic Usage\n\n\u003e **Note:** `mockClient()` mocks **all instances** of a client class. Use `mockClientInstance()` when you need to mock a specific instance.\n\n```typescript\nimport { describe, test, expect, beforeEach, afterEach } from \"vitest\";\nimport { mockClient } from \"aws-sdk-vitest-mock\";\nimport { S3Client, GetObjectCommand } from \"@aws-sdk/client-s3\";\n\n// Your application code\nclass DocumentService {\n  constructor(private s3Client: S3Client) {}\n\n  async getDocument(bucket: string, key: string) {\n    const result = await this.s3Client.send(\n      new GetObjectCommand({ Bucket: bucket, Key: key }),\n    );\n    return result.Body;\n  }\n}\n\ndescribe(\"DocumentService\", () =\u003e {\n  let s3Mock: ReturnType\u003ctypeof mockClient\u003e;\n  let documentService: DocumentService;\n\n  beforeEach(() =\u003e {\n    // Mock all instances of S3Client\n    s3Mock = mockClient(S3Client);\n\n    // Any S3Client instance created after this will be mocked\n    const s3Client = new S3Client({ region: \"us-east-1\" });\n    documentService = new DocumentService(s3Client);\n  });\n\n  afterEach(() =\u003e {\n    s3Mock.restore();\n  });\n\n  test(\"should retrieve document from S3\", async () =\u003e {\n    // Configure mock response\n    s3Mock.on(GetObjectCommand).resolves({\n      Body: \"document content\",\n      ContentType: \"text/plain\",\n    });\n\n    // Test your application code\n    const result = await documentService.getDocument(\"my-bucket\", \"doc.txt\");\n\n    expect(result).toBe(\"document content\");\n    expect(s3Mock).toHaveReceivedCommand(GetObjectCommand);\n  });\n});\n```\n\n## 🎯 Key Concepts\n\nUnderstanding these concepts will help you use the library effectively:\n\n- **`mockClient(ClientClass)`** - Mocks **all instances** of a client class. Use this in most test scenarios where you control client creation.\n- **`mockClientInstance(instance)`** - Mocks a **specific client instance**. Use when the client is created outside your test (e.g., in application bootstrap).\n- **Command Matching** - Commands are matched by constructor. Optionally match by input properties (partial matching by default, strict matching available).\n- **Sequential Responses** - Use `resolvesOnce()` / `rejectsOnce()` for one-time behaviors that fall back to permanent handlers set with `resolves()` / `rejects()`.\n- **Chainable API** - All mock configuration methods return the stub, allowing method chaining for cleaner test setup.\n- **Test Lifecycle**:\n  - **`reset()`** - Clears call history while preserving mock configurations. Use when you want to verify multiple test scenarios with the same mock setup.\n  - **`restore()`** - Completely removes mocking and restores original client behavior. Use in `afterEach()` to clean up between tests.\n\n## 📖 Usage Guide\n\n### Request Matching\n\n```typescript\n// Partial matching (default)\ns3Mock.on(GetObjectCommand, { Bucket: \"bucket1\" }).resolves({ Body: \"data1\" });\n\ns3Mock.on(GetObjectCommand, { Bucket: \"bucket2\" }).resolves({ Body: \"data2\" });\n\n// Strict matching\ns3Mock\n  .on(GetObjectCommand, { Bucket: \"b\", Key: \"k\" }, { strict: true })\n  .resolves({ Body: \"exact match\" });\n```\n\n### Sequential Responses\n\n```typescript\ns3Mock\n  .on(GetObjectCommand)\n  .resolvesOnce({ Body: \"first call\" })\n  .resolvesOnce({ Body: \"second call\" })\n  .resolves({ Body: \"subsequent calls\" });\n\n// First call returns 'first call'\n// Second call returns 'second call'\n// All other calls return 'subsequent calls'\n```\n\n### Error Handling\n\n```typescript\ns3Mock.on(GetObjectCommand).rejects(new Error(\"Not found\"));\n\n// Or with rejectsOnce\ns3Mock\n  .on(GetObjectCommand)\n  .rejectsOnce(new Error(\"Temporary failure\"))\n  .resolves({ Body: \"success\" });\n```\n\n### Custom Handlers\n\n```typescript\ns3Mock.on(GetObjectCommand).callsFake(async (input, getClient) =\u003e {\n  const client = getClient();\n  console.log(\"Bucket:\", input.Bucket);\n  return { Body: `Dynamic response for ${input.Key}` };\n});\n```\n\n### Mocking Existing Instances\n\nUse `mockClientInstance()` when you need to mock a client that's already been created:\n\n```typescript\n// Your application service that uses an injected S3 client\nclass FileUploadService {\n  constructor(private s3Client: S3Client) {}\n\n  async uploadFile(bucket: string, key: string, data: string) {\n    return await this.s3Client.send(\n      new PutObjectCommand({ Bucket: bucket, Key: key, Body: data }),\n    );\n  }\n}\n\ntest(\"should mock existing S3 client instance\", async () =\u003e {\n  // Client is already created (e.g., in application bootstrap)\n  const s3Client = new S3Client({ region: \"us-east-1\" });\n  const service = new FileUploadService(s3Client);\n\n  // Mock the specific client instance\n  const mock = mockClientInstance(s3Client);\n  mock.on(PutObjectCommand).resolves({ ETag: \"mock-etag\" });\n\n  // Test your service\n  const result = await service.uploadFile(\"bucket\", \"key\", \"data\");\n\n  expect(result.ETag).toBe(\"mock-etag\");\n  expect(mock).toHaveReceivedCommand(PutObjectCommand);\n});\n```\n\n### Test Lifecycle Management\n\nUse `reset()` to clear call history between assertions while keeping mock configurations. Use `restore()` to completely clean up mocking:\n\n```typescript\ntest(\"should handle multiple operations with same mock\", async () =\u003e {\n  const s3Mock = mockClient(S3Client);\n  const client = new S3Client({});\n\n  // Configure mock once\n  s3Mock.on(GetObjectCommand).resolves({ Body: \"file-content\" });\n\n  // First operation\n  await client.send(\n    new GetObjectCommand({ Bucket: \"bucket\", Key: \"file1.txt\" }),\n  );\n  expect(s3Mock).toHaveReceivedCommandTimes(GetObjectCommand, 1);\n\n  // Reset clears call history but keeps mock configuration\n  s3Mock.reset();\n  expect(s3Mock).toHaveReceivedCommandTimes(GetObjectCommand, 0);\n\n  // Second operation - mock still works\n  await client.send(\n    new GetObjectCommand({ Bucket: \"bucket\", Key: \"file2.txt\" }),\n  );\n  expect(s3Mock).toHaveReceivedCommandTimes(GetObjectCommand, 1);\n\n  // Clean up completely\n  s3Mock.restore();\n});\n```\n\n## 🔧 AWS Service Examples\n\n### DynamoDB with Marshal/Unmarshal\n\nMock DynamoDB operations using marshal/unmarshal utilities for type-safe data handling:\n\n```typescript\nimport { describe, test, expect, beforeEach, afterEach } from \"vitest\";\nimport { mockClient } from \"aws-sdk-vitest-mock\";\nimport {\n  DynamoDBClient,\n  GetItemCommand,\n  PutItemCommand,\n} from \"@aws-sdk/client-dynamodb\";\nimport { marshall, unmarshall } from \"@aws-sdk/util-dynamodb\";\n\n// Your application service\nclass UserService {\n  constructor(private dynamoClient: DynamoDBClient) {}\n\n  async getUser(userId: string) {\n    const result = await this.dynamoClient.send(\n      new GetItemCommand({\n        TableName: \"Users\",\n        Key: marshall({ id: userId }),\n      }),\n    );\n\n    return result.Item ? unmarshall(result.Item) : null;\n  }\n\n  async createUser(user: { id: string; name: string; email: string }) {\n    await this.dynamoClient.send(\n      new PutItemCommand({\n        TableName: \"Users\",\n        Item: marshall(user),\n      }),\n    );\n  }\n}\n\ndescribe(\"UserService with DynamoDB\", () =\u003e {\n  let dynamoMock: ReturnType\u003ctypeof mockClient\u003e;\n  let userService: UserService;\n\n  beforeEach(() =\u003e {\n    dynamoMock = mockClient(DynamoDBClient);\n    const dynamoClient = new DynamoDBClient({ region: \"us-east-1\" });\n    userService = new UserService(dynamoClient);\n  });\n\n  afterEach(() =\u003e {\n    dynamoMock.restore();\n  });\n\n  test(\"should get user by id\", async () =\u003e {\n    const mockUser = { id: \"123\", name: \"John Doe\", email: \"john@example.com\" };\n\n    // Mock DynamoDB response with marshalled data\n    dynamoMock.on(GetItemCommand).resolves({\n      Item: marshall(mockUser),\n    });\n\n    const result = await userService.getUser(\"123\");\n\n    expect(result).toEqual(mockUser);\n    expect(dynamoMock).toHaveReceivedCommandWith(GetItemCommand, {\n      TableName: \"Users\",\n      Key: marshall({ id: \"123\" }),\n    });\n  });\n\n  test(\"should create new user\", async () =\u003e {\n    const newUser = {\n      id: \"456\",\n      name: \"Jane Smith\",\n      email: \"jane@example.com\",\n    };\n\n    dynamoMock.on(PutItemCommand).resolves({});\n\n    await userService.createUser(newUser);\n\n    expect(dynamoMock).toHaveReceivedCommandWith(PutItemCommand, {\n      TableName: \"Users\",\n      Item: marshall(newUser),\n    });\n  });\n\n  test(\"should return null for non-existent user\", async () =\u003e {\n    dynamoMock.on(GetItemCommand).resolves({}); // No Item in response\n\n    const result = await userService.getUser(\"999\");\n\n    expect(result).toBeNull();\n  });\n});\n```\n\n## 🚀 Advanced Features\n\n### Stream Mocking (S3)\n\nMock S3 operations that return streams with automatic environment detection:\n\n```typescript\n// Mock with string content\ns3Mock.on(GetObjectCommand).resolvesStream(\"Hello, World!\");\n\n// Mock with Buffer\ns3Mock.on(GetObjectCommand).resolvesStream(Buffer.from(\"Binary data\"));\n\n// One-time stream response\ns3Mock\n  .on(GetObjectCommand)\n  .resolvesStreamOnce(\"First call\")\n  .resolvesStream(\"Subsequent calls\");\n```\n\n### Paginator Support\n\nMock AWS SDK v3 pagination with automatic token handling. **Tokens are the actual last item from each page** (works for both DynamoDB and S3).\n\n#### DynamoDB Pagination\n\n```typescript\nimport { marshall, unmarshall } from '@aws-sdk/util-dynamodb';\nimport { DynamoDBClient, ScanCommand } from '@aws-sdk/client-dynamodb';\n\n// Create marshalled items (as they would be stored in DynamoDB)\nconst users = [\n  { id: \"user-1\", name: \"Alice\", email: \"alice@example.com\" },\n  { id: \"user-2\", name: \"Bob\", email: \"bob@example.com\" },\n  { id: \"user-3\", name: \"Charlie\", email: \"charlie@example.com\" },\n];\n\nconst marshalledUsers = users.map(user =\u003e marshall(user));\n\n// Configure pagination\ndynamoMock.on(ScanCommand).resolvesPaginated(marshalledUsers, {\n  pageSize: 1,\n  itemsKey: \"Items\",\n  tokenKey: \"LastEvaluatedKey\",      // DynamoDB response key\n  inputTokenKey: \"ExclusiveStartKey\"  // DynamoDB request key\n});\n\n// Page 1: Get first user\nconst page1 = await client.send(new ScanCommand({ TableName: \"Users\" }));\nexpect(page1.Items).toHaveLength(1);\n// LastEvaluatedKey is the marshalled last item (object, not string!)\nexpect(page1.LastEvaluatedKey).toEqual(marshall({ id: \"user-1\", name: \"Alice\", ... }));\n\n// Unmarshall the items\nconst page1Users = page1.Items.map(item =\u003e unmarshall(item));\nconsole.log(page1Users[0]); // { id: \"user-1\", name: \"Alice\", ... }\n\n// Page 2: Use LastEvaluatedKey to get next page\nconst page2 = await client.send(\n  new ScanCommand({\n    TableName: \"Users\",\n    ExclusiveStartKey: page1.LastEvaluatedKey, // Pass the object directly\n  })\n);\n\n// Page 3: Continue until LastEvaluatedKey is undefined\nconst page3 = await client.send(\n  new ScanCommand({\n    TableName: \"Users\",\n    ExclusiveStartKey: page2.LastEvaluatedKey,\n  })\n);\nexpect(page3.LastEvaluatedKey).toBeUndefined(); // No more pages\n```\n\n#### S3 Pagination\n\n```typescript\nimport { S3Client, ListObjectsV2Command } from '@aws-sdk/client-s3';\n\nconst objects = Array.from({ length: 100 }, (_, i) =\u003e ({\n  Key: `file-${i + 1}.txt`,\n  Size: 1024,\n  LastModified: new Date(),\n}));\n\ns3Mock.on(ListObjectsV2Command).resolvesPaginated(objects, {\n  pageSize: 50,\n  itemsKey: \"Contents\",\n  tokenKey: \"NextContinuationToken\",\n  inputTokenKey: \"ContinuationToken\"\n});\n\n// First page\nconst page1 = await client.send(\n  new ListObjectsV2Command({ Bucket: \"my-bucket\" })\n);\nexpect(page1.Contents).toHaveLength(50);\n// NextContinuationToken is the last object from page 1\nexpect(page1.NextContinuationToken).toEqual({ Key: \"file-50.txt\", ... });\n\n// Second page\nconst page2 = await client.send(\n  new ListObjectsV2Command({\n    Bucket: \"my-bucket\",\n    ContinuationToken: page1.NextContinuationToken,\n  })\n);\nexpect(page2.Contents).toHaveLength(50);\nexpect(page2.NextContinuationToken).toBeUndefined(); // No more pages\n```\n\n**Pagination Options:**\n\n- `pageSize` - Number of items per page (default: 10)\n- `itemsKey` - Property name for items array in response (default: \"Items\")\n- `tokenKey` - Property name for pagination token in response (default: \"NextToken\")\n  - DynamoDB: use `\"LastEvaluatedKey\"`\n  - S3: use `\"NextContinuationToken\"`\n- `inputTokenKey` - Property name for pagination token in request (defaults to same as tokenKey)\n  - DynamoDB: use `\"ExclusiveStartKey\"`\n  - S3: use `\"ContinuationToken\"`\n\n**How It Works:**\n\nThe mock automatically uses the **last item from each page** as the pagination token. This means:\n\n- ✅ For DynamoDB: `LastEvaluatedKey` is a proper object (can be unmarshalled)\n- ✅ For S3: `NextContinuationToken` is the last object\n- ✅ Tokens represent actual data, not opaque strings\n- ✅ Works correctly with `unmarshall()` for DynamoDB\n  - Use this when AWS service uses different names for input/output tokens (e.g., DynamoDB's `ExclusiveStartKey` vs `LastEvaluatedKey`)\n\n### AWS Error Simulation\n\nConvenient helper methods for common AWS errors:\n\n```typescript\n// S3 Errors\ns3Mock.on(GetObjectCommand).rejectsWithNoSuchKey(\"missing-key\");\ns3Mock.on(GetObjectCommand).rejectsWithNoSuchBucket(\"missing-bucket\");\ns3Mock.on(GetObjectCommand).rejectsWithAccessDenied(\"protected-resource\");\n\n// DynamoDB Errors\ndynamoMock.on(GetItemCommand).rejectsWithResourceNotFound(\"missing-table\");\ndynamoMock.on(PutItemCommand).rejectsWithConditionalCheckFailed();\n\n// General AWS Errors\ns3Mock.on(GetObjectCommand).rejectsWithThrottling();\ns3Mock.on(GetObjectCommand).rejectsWithInternalServerError();\n```\n\n### Delay/Latency Simulation\n\nSimulate network delays for testing timeouts and race conditions:\n\n```typescript\n// Resolve with delay\ns3Mock.on(GetObjectCommand).resolvesWithDelay({ Body: \"data\" }, 1000);\n\n// Reject with delay\ns3Mock.on(GetObjectCommand).rejectsWithDelay(\"Network timeout\", 500);\n```\n\n### Fixture Loading\n\nLoad mock responses from files for easier test data management:\n\n```typescript\n// Load JSON response from file (automatically parsed)\ns3Mock.on(GetObjectCommand).resolvesFromFile(\"./fixtures/s3-response.json\");\n\n// Load text response from file (returned as string)\ns3Mock.on(GetObjectCommand).resolvesFromFile(\"./fixtures/response.txt\");\n```\n\n### Debug Mode\n\nEnable debug logging to see detailed information about mock configuration, lifecycle events, and command interactions:\n\n```typescript\nconst s3Mock = mockClient(S3Client);\n\n// Enable debug logging\ns3Mock.enableDebug();\n\n// Configuration logs appear immediately:\n// [aws-sdk-vitest-mock](Debug) Configured resolves for GetObjectCommand\n// {\n//   \"matcher\": {\n//     \"Bucket\": \"test-bucket\"\n//   },\n//   \"strict\": false\n// }\ns3Mock\n  .on(GetObjectCommand, { Bucket: \"test-bucket\" })\n  .resolves({ Body: \"data\" });\n\n// Interaction logs appear when commands are sent:\n// [aws-sdk-vitest-mock](Debug) Received command: GetObjectCommand\n// {\n//   \"Bucket\": \"test-bucket\",\n//   \"Key\": \"file.txt\"\n// }\n// [aws-sdk-vitest-mock](Debug) Found 1 mock(s) for GetObjectCommand\n// [aws-sdk-vitest-mock](Debug) Using mock at index 0 for GetObjectCommand\nawait client.send(\n  new GetObjectCommand({ Bucket: \"test-bucket\", Key: \"file.txt\" }),\n);\n\n// Lifecycle logs:\n// [aws-sdk-vitest-mock](Debug) Clearing call history (mocks preserved)\ns3Mock.reset();\n\n// [aws-sdk-vitest-mock](Debug) Restoring original client behavior and clearing all mocks\ns3Mock.restore();\n\n// Disable debug logging\ns3Mock.disableDebug();\n```\n\n#### Global Debug Configuration\n\nEnable debug logging for all mocks globally, with the ability to override at the individual mock level:\n\n```typescript\nimport { setGlobalDebug, mockClient } from \"aws-sdk-vitest-mock\";\nimport { S3Client, GetObjectCommand } from \"@aws-sdk/client-s3\";\nimport { DynamoDBClient, GetItemCommand } from \"@aws-sdk/client-dynamodb\";\n\n// Enable debug for all mocks\nsetGlobalDebug(true);\n\n// All mocks will inherit the global debug setting\nconst s3Mock = mockClient(S3Client);\nconst dynamoMock = mockClient(DynamoDBClient);\n\n// Both mocks will log debug information\ns3Mock.on(GetObjectCommand).resolves({ Body: \"data\" });\ndynamoMock.on(GetItemCommand).resolves({ Item: { id: { S: \"1\" } } });\n\n// Override global setting for a specific mock\ns3Mock.disableDebug(); // This mock won't log, but dynamoMock still will\n\n// Disable global debug\nsetGlobalDebug(false);\n```\n\n**Debug Priority (highest to lowest):**\n\n1. Individual mock's `enableDebug()` or `disableDebug()` call (explicit override)\n2. Global debug setting via `setGlobalDebug()`\n3. Default: disabled\n\n**Key behaviors:**\n\n- When global debug is enabled, all new and existing mocks will log unless explicitly disabled\n- Individual mock settings always take priority over global settings\n- `reset()` preserves individual debug settings\n- Global debug can be changed at any time and affects all mocks without explicit settings\n\nDebug mode provides comprehensive logging for:\n\n**Mock Configuration:**\n\n- Mock setup with `.on()`, `.resolves()`, `.rejects()`, `.callsFake()`, etc.\n- Matcher details and strict mode settings\n- Paginated response configuration\n- File-based fixture loading\n\n**Mock Interactions:**\n\n- Incoming commands and their inputs\n- Number of configured mocks for each command\n- Mock matching results and reasons for failures\n- One-time mock removal notifications\n\n**Lifecycle Events:**\n\n- Reset operations (clearing call history)\n- Restore operations (removing all mocks)\n\n## 🧪 Test Coverage\n\nThe library includes comprehensive test suites covering all features:\n\n- **Core mocking functionality** - Command matching, response handling, sequential responses\n- **Paginator support** - Automatic token handling for AWS pagination patterns\n- **Debug logging** - Enable/disable functionality and proper console output formatting\n- **Stream mocking** - S3 stream responses with environment detection\n- **Error simulation** - AWS-specific errors and general error handling\n- **Custom matchers** - Vitest integration for asserting command calls\n\nAll utilities have dedicated test files ensuring reliability and maintainability.\n\n## 🧪 Custom Matchers\n\nImport the custom matchers in your test setup:\n\n```typescript\n// vitest.setup.ts\nimport \"aws-sdk-vitest-mock/vitest-setup\";\n```\n\nThen use them in your tests:\n\n```typescript\nimport { expect, test } from \"vitest\";\nimport { mockClient } from \"aws-sdk-vitest-mock\";\nimport { DynamoDBClient, GetItemCommand } from \"@aws-sdk/client-dynamodb\";\n\ntest(\"should call DynamoDB\", async () =\u003e {\n  const ddbMock = mockClient(DynamoDBClient);\n  ddbMock.on(GetItemCommand).resolves({ Item: { id: { S: \"123\" } } });\n\n  const client = new DynamoDBClient({});\n  await client.send(\n    new GetItemCommand({\n      TableName: \"users\",\n      Key: { id: { S: \"123\" } },\n    }),\n  );\n\n  // Assert the command was called\n  expect(ddbMock).toHaveReceivedCommand(GetItemCommand);\n\n  // Assert it was called a specific number of times\n  expect(ddbMock).toHaveReceivedCommandTimes(GetItemCommand, 1);\n\n  // Assert it was called with specific input\n  expect(ddbMock).toHaveReceivedCommandWith(GetItemCommand, {\n    TableName: \"users\",\n    Key: { id: { S: \"123\" } },\n  });\n\n  // Assert the nth call had specific input\n  expect(ddbMock).toHaveReceivedNthCommandWith(1, GetItemCommand, {\n    TableName: \"users\",\n  });\n\n  // Assert no other commands were received\n  expect(ddbMock).toHaveReceivedNoOtherCommands([GetItemCommand]);\n});\n```\n\n## 📚 API Reference\n\n\u003e TypeScript documentation for this library can be found at [here](https://sudokar.github.io/aws-sdk-vitest-mock/)\n\n### `mockClient\u003cTClient\u003e(ClientConstructor)`\n\nCreates a mock for an AWS SDK client constructor.\n\n**Returns:** `AwsClientStub\u003cTClient\u003e`\n\n### `mockClientInstance\u003cTClient\u003e(clientInstance)`\n\nMocks an existing AWS SDK client instance.\n\n**Returns:** `AwsClientStub\u003cTClient\u003e`\n\n### Global Debug Functions\n\n- `setGlobalDebug(enabled: boolean)` - Enable or disable debug logging globally for all mocks\n\n### `AwsClientStub` Methods\n\n- `on(Command, matcher?, options?)` - Configure mock for a command\n- `reset()` - Clear call history while preserving mock configurations\n- `restore()` - Restore original client behavior\n- `calls()` - Get call history\n- `enableDebug()` - Enable debug logging for troubleshooting (overrides global setting)\n- `disableDebug()` - Disable debug logging (overrides global setting)\n\n### `AwsCommandStub` Methods (Chainable)\n\n- `resolves(output)` - Return successful response\n- `resolvesOnce(output)` - Return successful response once\n- `rejects(error)` - Return error\n- `rejectsOnce(error)` - Return error once\n- `callsFake(handler)` - Custom response handler\n- `callsFakeOnce(handler)` - Custom response handler (once)\n- `resolvesStream(data)` - Return stream response (S3 helper)\n- `resolvesStreamOnce(data)` - Return stream response once (S3 helper)\n- `resolvesWithDelay(output, delayMs)` - Return response with delay\n- `rejectsWithDelay(error, delayMs)` - Return error with delay\n- `resolvesPaginated(items, options?)` - Return paginated responses with automatic token handling\n- `resolvesFromFile(filePath)` - Load response from file (JSON files are parsed, others returned as strings)\n- `rejectsWithNoSuchKey(key?)` - Reject with S3 NoSuchKey error\n- `rejectsWithNoSuchBucket(bucket?)` - Reject with S3 NoSuchBucket error\n- `rejectsWithAccessDenied(resource?)` - Reject with AccessDenied error\n- `rejectsWithResourceNotFound(resource?)` - Reject with DynamoDB ResourceNotFound error\n- `rejectsWithConditionalCheckFailed()` - Reject with DynamoDB ConditionalCheckFailed error\n- `rejectsWithThrottling()` - Reject with Throttling error\n- `rejectsWithInternalServerError()` - Reject with InternalServerError\n\n## 🤝 Contributing\n\nWe welcome contributions! 🎉 Please read our [Contributing Guidelines](./CONTRIBUTING.md) for details on:\n\n- 🐛 Reporting bugs\n- 💡 Suggesting features\n- 🔧 Development setup\n- ✅ Code standards\n- 📝 Commit guidelines\n- 🚀 Pull request process\n\n### Quick Start for Contributors\n\n```bash\n# Fork and clone the repo\ngit clone https://github.com/YOUR-USERNAME/aws-sdk-vitest-mock.git\ncd aws-sdk-vitest-mock\n\n# Install dependencies\nbun install\n\n# Run tests\nbun nx test\n\n# Run linting\nbun nx lint\n\n# Build the library\nbun nx build\n\n# Make your changes and submit a PR!\n```\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md) for the complete guide.\n\n## Acknowledgements\n\nThis library is based on the core ideas and API patterns introduced by [aws-sdk-client-mock](https://github.com/m-radzikowski/aws-sdk-client-mock), which is no longer actively maintained.\n\nIt reimagines those concepts for Vitest, while extending them with additional features, improved ergonomics, and ongoing maintenance.\n\n## 📝 License\n\nMIT\n\n## 🔗 Links\n\n- [GitHub Repository](https://github.com/sudokar/aws-sdk-vitest-mock)\n- [Issue Tracker](https://github.com/sudokar/aws-sdk-vitest-mock/issues)\n- [Changelog](https://github.com/sudokar/aws-sdk-vitest-mock/releases)\n\n---\n\n**Made with ❤️ by [sudokar](https://github.com/sudokar)**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsudokar%2Faws-sdk-vitest-mock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsudokar%2Faws-sdk-vitest-mock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsudokar%2Faws-sdk-vitest-mock/lists"}