Skip to content

Refactor key types #1917

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open

Conversation

flevi29
Copy link
Collaborator

@flevi29 flevi29 commented Apr 5, 2025

Pull Request

What does this PR do?

Caution

Breaking changes

  • Date types are now string types, conversion has to be done manually
  • renamed some types:
    • KeysQuery -> ListApiKeys
    • KeysResults -> KeyViewList
    • Key -> KeyView
    • KeyCreation -> CreateApiKey
    • KeyUpdate -> PatchApiKey

PR checklist

Please check if your PR fulfills the following requirements:

  • Does this PR fix an existing issue, or have you listed the changes applied in the PR description (and why they are needed)?
  • Have you read the contributing guidelines?
  • Have you made sure that the title is accurate and descriptive of the changes?

Thank you so much for contributing to Meilisearch!

Summary by CodeRabbit

  • Refactor
    • Updated API key management methods with enhanced type specificity and direct API responses.
    • Modernized and reorganized API key-related type definitions for clarity and precision.
  • Tests
    • Simplified and restructured API key tests using data-driven approaches and new assertion helpers.
  • Chores
    • Added utility functions to improve handling of object keys and entries in test utilities.

@flevi29 flevi29 added the breaking-change The related changes are breaking for the users label Apr 5, 2025
Copy link

codecov bot commented Apr 5, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 99.03%. Comparing base (77505c0) to head (79c88e6).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1917      +/-   ##
==========================================
- Coverage   99.03%   99.03%   -0.01%     
==========================================
  Files          18       18              
  Lines        1449     1447       -2     
  Branches      305      303       -2     
==========================================
- Hits         1435     1433       -2     
  Misses         14       14              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@flevi29 flevi29 requested a review from Copilot April 6, 2025 08:00
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

@flevi29 flevi29 requested a review from Copilot April 6, 2025 08:35
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

@flevi29 flevi29 mentioned this pull request Apr 9, 2025
8 tasks
@flevi29 flevi29 requested a review from Strift May 13, 2025 09:03
Copy link

coderabbitai bot commented May 19, 2025

Walkthrough

The changes overhaul the API key management types and methods in the codebase. Types related to API keys are refactored and moved to a new module, and the MeiliSearch class methods are updated to use these new types. The test suite for API key management is rewritten to use data-driven parameterized tests and new assertion helpers. Utility functions for typed object key and entry operations are added.

Changes

File(s) Change Summary
src/meilisearch.ts Refactored API key management methods to use new, more specific types for parameters and return values. Updated documentation links and removed obsolete imports.
src/types/keys.ts Introduced a new module defining all API key-related types, including actions, creation/update payloads, key views, pagination, and patch types.
src/types/index.ts Added export of all types from the new keys.ts module.
src/types/types.ts Removed all legacy API key types, queries, and result type definitions.
tests/keys.test.ts Completely refactored API key tests to use parameterized, data-driven tests with custom assertion helpers. Removed permission-level tests and setup/teardown hooks.
tests/utils/meilisearch-test-utils.ts Added new utility functions objectKeys and objectEntries for typed object key and entry operations, and exported them.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant MeiliSearch
    participant HTTP_API

    Client->>MeiliSearch: getKeys(listApiKeys)
    MeiliSearch->>HTTP_API: GET /keys with params
    HTTP_API-->>MeiliSearch: KeyViewList (raw)
    MeiliSearch-->>Client: KeyViewList

    Client->>MeiliSearch: createKey(createApiKey)
    MeiliSearch->>HTTP_API: POST /keys with payload
    HTTP_API-->>MeiliSearch: KeyView (raw)
    MeiliSearch-->>Client: KeyView

    Client->>MeiliSearch: updateKey(keyUid, patchApiKey)
    MeiliSearch->>HTTP_API: PATCH /keys/:keyUid
    HTTP_API-->>MeiliSearch: KeyView (raw)
    MeiliSearch-->>Client: KeyView

    Client->>MeiliSearch: getKey(keyUid)
    MeiliSearch->>HTTP_API: GET /keys/:keyUid
    HTTP_API-->>MeiliSearch: KeyView (raw)
    MeiliSearch-->>Client: KeyView

    Client->>MeiliSearch: deleteKey(keyUid)
    MeiliSearch->>HTTP_API: DELETE /keys/:keyUid
    HTTP_API-->>MeiliSearch: void
    MeiliSearch-->>Client: void
Loading

Poem

🗝️

Refactored keys, a tidy warren,
Types are crisp, no longer foreign.
Tests now hop from field to field,
With helpers new, correctness sealed.
In every burrow, types align,
This bunny’s code is quite divine!

🐇


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@flevi29
Copy link
Collaborator Author

flevi29 commented May 19, 2025

@coderabbitai review

Copy link

coderabbitai bot commented May 19, 2025

✅ 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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (7)
tests/keys.test.ts (7)

17-18: Reduce brittleness of the structural assertion

Hard-coding the exact property count (9) makes the test fragile:
any harmless future addition of a field in KeyView will break the whole suite.

-extAssert.lengthOf(Object.keys(value), 9);
+extAssert.isAtLeast(Object.keys(value).length, 9);

By checking at least the required fields you still guarantee the current contract while allowing non-breaking extensions.


30-37: Nit: consolidate repeating assertions

The name and description checks duplicate the same null-or-string logic.
Packaging them in a tiny helper (e.g. isNullableString) keeps isKeyView short and self-documenting.

-  extAssert(
-    name === null || typeof name === "string",
-    "expected name to be null or string",
-  );
+  const isNullableString = (v: unknown) =>
+    v === null || typeof v === "string";
+  extAssert(isNullableString(name), "expected name to be null or string");

55-56: Consider validating ISO-8601 timestamps

createdAt and updatedAt must be ISO strings, not just any string.

-extAssert.typeOf(createdAt, "string");
-extAssert.typeOf(updatedAt, "string");
+const isoRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z$/;
+extAssert.match(createdAt, isoRegex);
+extAssert.match(updatedAt, isoRegex);

This prevents silent acceptance of malformed dates.


60-63: Shadowing may confuse intellisense

Re-exporting assert with additional methods is handy but can bewilder IDE
resolution (assert now has more members than vitest’s type knows).

If you keep this pattern often, consider creating a dedicated assertion module
(e.g. tests/assert.ts) that explicitly merges and exports once, then import it everywhere.


178-182: Mutation inside data-setup loop obscures intent

Mutating the name element (testValue[0]) in-place makes the data harder to
follow. Compute the formatted title on the fly to keep the source immutable:

-for (const testValues of Object.values(testRecord)) {
-  for (const testValue of testValues) {
-    testValue[0] = testValue[0] === undefined ? "" : ` with "${testValue[0]}"`;
-  }
-}
+const formatTitle = (raw: string | undefined) =>
+  raw === undefined ? "" : ` with "${raw}"`;

Then call formatTitle(...) where you build the test name.


184-184: Top-level await ties the file to ESM

await getClient() at the file root requires ESM support in the test runner.
Vitest handles this, but the pattern prevents running the same tests in other
runners (e.g. Jest) or under CommonJS tools.

Safest alternative:

let ms: MeiliSearch;
beforeAll(async () => {
  ms = await getClient("Master");
});

190-195: Duplicate object keys rely on evaluation order

When key === "actions" (or expiresAt) the spread later in the literal
overrides earlier values. While it currently works, the mix of defaults plus
conditional overwrite invites subtle bugs.

Consider constructing the request with a shallow merge:

const body: CreateApiKey = {
  actions: ["*"],
  indexes: ["*"],
  expiresAt: null,
  ...({ [key]: value } as Partial<CreateApiKey>),
};
const keyView = await ms.createKey(body);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 77505c0 and c2b875a.

📒 Files selected for processing (6)
  • src/meilisearch.ts (2 hunks)
  • src/types/index.ts (1 hunks)
  • src/types/keys.ts (1 hunks)
  • src/types/types.ts (0 hunks)
  • tests/keys.test.ts (1 hunks)
  • tests/utils/meilisearch-test-utils.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/types/types.ts
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/meilisearch.ts (1)
src/types/keys.ts (5)
  • ListApiKeys (64-67)
  • KeyViewList (78-78)
  • KeyView (47-57)
  • CreateApiKey (33-40)
  • PatchApiKey (85-85)
src/types/keys.ts (1)
src/http-requests.ts (1)
  • T (199-264)
tests/keys.test.ts (2)
src/types/keys.ts (4)
  • KeyView (47-57)
  • CreateApiKey (33-40)
  • Action (12-26)
  • ListApiKeys (64-67)
tests/utils/meilisearch-test-utils.ts (4)
  • assert (131-134)
  • objectKeys (257-257)
  • getClient (269-269)
  • objectEntries (258-258)
🔇 Additional comments (18)
tests/utils/meilisearch-test-utils.ts (3)

247-249: Well-implemented type-safe helper function.

This is a useful utility function that extracts keys from an object with string literal keys, returning them as a typed array. This ensures type safety when working with object keys in tests.


251-254: Nice type-safety enhancement for Object.entries.

This typed alias for Object.entries is a good addition that preserves precise key and value typings for record types, which the native Object.entries doesn't provide. The proper typing will help catch potential errors during development.


257-258: Good export of new utility functions.

Properly exposing the new utility functions through exports makes them available for use in test files.

src/types/index.ts (1)

2-2: Properly exports the new keys module.

The addition of this export statement ensures that the newly created key-related types from keys.js are accessible through the central types index, which is essential for their use throughout the codebase.

src/types/keys.ts (8)

1-6: Good use of type aliases for better readability.

The use of type aliases for common string literals improves code readability and maintainability. These will be useful when composing the more complex Action type.


12-26: Well-structured permission actions with template literal types.

The Action type effectively uses template literal types to create a comprehensive and type-safe representation of all possible API key permissions. This approach is more precise than using simple string literals and helps prevent errors when specifying permissions.


33-40: Clear API key creation structure with proper documentation.

The CreateApiKey type accurately represents the payload structure required to create a new API key, with proper JSDoc documentation linking to official resources. Note that the expiresAt field is now a string instead of Date, which is a breaking change as mentioned in the PR description.


47-57: Good API key view model with string timestamp representation.

The KeyView type correctly represents the API key object returned by the server. The timestamps (createdAt, updatedAt, expiresAt) are defined as strings rather than Date objects, which aligns with the server response but is a breaking change for consumers that expect Date objects. This change is mentioned in the PR description.


64-67: Simple and effective pagination parameters.

The ListApiKeys type provides a clean interface for optional pagination parameters when listing keys.


69-75: Reusable pagination view type.

Creating a generic PaginationView<T> type is a good practice for reusability across different paginated resources. This approach allows for consistent pagination handling across the API.


77-78: Concise key list type using composition.

Using the generic PaginationView<T> to create the KeyViewList type is a good example of type composition. This approach makes the code more maintainable and ensures consistency.


80-85: Efficient use of Pick for limited updates.

The PatchApiKey type efficiently uses Pick to limit updatable fields to just name and description. This approach ensures type safety by preventing updates to fields that shouldn't be modified.

src/meilisearch.ts (6)

10-31: Updated imports to include the new key-related types.

The import statement has been properly updated to include the new key-related types from the keys module. Type imports are organized logically, which helps with code readability.


306-311: Updated getKeys method with new type signatures.

The getKeys method has been updated to use the new ListApiKeys and KeyViewList types, which provide more precise type checking. The function documentation has been updated to use a concise link to the official API reference, which is a good practice.


314-319: Updated getKey method with new return type.

The getKey method now returns KeyView instead of the deprecated Key type, aligning with the new type system. The function documentation has been updated to use a concise link to the official API reference.


321-327: Updated createKey method with new parameter and return types.

The createKey method now accepts CreateApiKey and returns KeyView, aligning with the new type system. The function documentation has been updated to use a concise link to the official API reference.


329-338: Updated updateKey method with new parameter and return types.

The updateKey method now accepts keyOrUid and PatchApiKey parameters and returns KeyView, aligning with the new type system. The function documentation has been updated to use a concise link to the official API reference.


340-340: Consistent documentation style for deleteKey method.

The deleteKey method's documentation has been updated to match the concise style of the other key-related methods, using a direct link to the official API reference. This ensures documentation consistency across related methods.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
tests/keys.test.ts (4)

16-59: Well-structured custom assertion helper

The isKeyView helper effectively encapsulates validation logic for API key objects, making tests more maintainable and consistent. This is a good application of the DRY principle.

A minor improvement would be to replace the individual field type assertions with a more generic property type validation approach to reduce repetition when checking string/null fields.

-    extAssert(
-      name === null || typeof name === "string",
-      "expected name to be null or string",
-    );
-    extAssert(
-      description === null || typeof description === "string",
-      "expected description to be null or string",
-    );
-
-    extAssert.typeOf(key, "string");
-    extAssert.typeOf(uid, "string");
-
-    for (const action of actions) {
-      extAssert.oneOf(action, possibleActions);
-    }
-
-    for (const index of indexes) {
-      extAssert.typeOf(index, "string");
-    }
-
-    extAssert(
-      expiresAt === null || typeof expiresAt === "string",
-      "expected expiresAt to be null or string",
-    );
-
-    extAssert.typeOf(createdAt, "string");
-    extAssert.typeOf(updatedAt, "string");
+    // Helper for nullable string validation
+    const isNullableString = (val: unknown, fieldName: string) =>
+      extAssert(
+        val === null || typeof val === "string",
+        `expected ${fieldName} to be null or string`
+      );
+    
+    // Validate nullable string fields
+    isNullableString(name, "name");
+    isNullableString(description, "description");
+    isNullableString(expiresAt, "expiresAt");
+    
+    // Validate string fields
+    extAssert.typeOf(key, "string");
+    extAssert.typeOf(uid, "string");
+    extAssert.typeOf(createdAt, "string");
+    extAssert.typeOf(updatedAt, "string");
+
+    // Validate arrays
+    actions.forEach(action => extAssert.oneOf(action, possibleActions));
+    indexes.forEach(index => extAssert.typeOf(index, "string"));

88-176: Comprehensive test data for API key properties

The test record provides a thorough set of test cases for all key properties, creating a solid foundation for data-driven tests. The assertions are correctly matched to each property's type.

However, there's a potential confusion with the expiresAt assertion where the non-null assertion operator (!) is used. It would be more type-safe to check for null first.

      (a, b) => {
-        assert.strictEqual(Date.parse(a!), Date.parse(b!));
+        assert.strictEqual(
+          a === null ? null : Date.parse(a),
+          b === null ? null : Date.parse(b)
+        );
      },

178-183: Test name transformation could be more flexible

This section transforms test names, but it makes assumptions about the structure of test values that might not hold if the data format changes.

Consider creating a more explicit mapping function that handles different test value formats more predictably:

// transform names
-for (const testValues of Object.values(testRecord)) {
-  for (const testValue of testValues) {
-    testValue[0] = testValue[0] === undefined ? "" : ` with "${testValue[0]}"`;
-  }
-}
+function formatTestName(name: string | undefined): string {
+  return name === undefined ? "" : ` with "${name}"`;
+}
+
+// Apply formatting to all test names
+for (const testValues of Object.values(testRecord)) {
+  for (const testValue of testValues) {
+    testValue[0] = formatTestName(testValue[0]);
+  }
+}

223-250: Combined test for multiple API operations

This test combines verification of getKeys, getKey, and deleteKey in a single test case. While functional, it makes tracking failures more difficult as a failure in one operation will obscure whether the others would have passed.

Consider separating these into individual test cases, potentially with a shared setup:

-test(`${ms.getKeys.name}, ${ms.getKey.name} and ${ms.deleteKey.name} methods`, async () => {
+test(`${ms.getKeys.name} should return a valid key list with correct pagination`, async () => {
   const keyList = await ms.getKeys({
     offset: 0,
     limit: 10_000,
   } satisfies Required<ListApiKeys>);
 
+  assert.lengthOf(Object.keys(keyList), 4);
+  const { results, offset, limit, total } = keyList;
+
+  for (const keyView of results) {
+    assert.isKeyView(keyView);
+  }
+
+  assert.typeOf(offset, "number");
+  assert.typeOf(limit, "number");
+  assert.typeOf(total, "number");
+});
+
+test(`${ms.getKey.name} should return valid individual keys`, async () => {
+  const keyList = await ms.getKeys({
+    limit: 10_000,
+  });
+
   for (const { uid, name } of keyList.results) {
     const keyView = await ms.getKey(uid);
+    assert.isKeyView(keyView);
+  }
+});
+
+test(`${ms.deleteKey.name} should delete non-default keys`, async () => {
+  const keyList = await ms.getKeys({
+    limit: 10_000,
+  });
+
+  for (const { uid, name } of keyList.results) {
     // avoid deleting default keys that might be used by other tests
     if (name !== "Default Search API Key" && name !== "Default Admin API Key") {
       await ms.deleteKey(uid);
     }
   }
-
-  assert.lengthOf(Object.keys(keyList), 4);
-  const { results, offset, limit, total } = keyList;
-
-  for (const keyView of results) {
-    assert.isKeyView(keyView);
-  }
-
-  assert.typeOf(offset, "number");
-  assert.typeOf(limit, "number");
-  assert.typeOf(total, "number");
 });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Lite
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between c2b875a and 79c88e6.

📒 Files selected for processing (1)
  • tests/keys.test.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/keys.test.ts (2)
src/types/keys.ts (4)
  • KeyView (47-57)
  • CreateApiKey (33-40)
  • Action (12-26)
  • ListApiKeys (64-67)
tests/utils/meilisearch-test-utils.ts (4)
  • assert (131-134)
  • objectKeys (257-257)
  • getClient (269-269)
  • objectEntries (258-258)
🔇 Additional comments (4)
tests/keys.test.ts (4)

66-66: UUID is now dynamically generated

Great! Using randomUUID() addresses the previous review comment about potential collisions between test runs.


68-86: Excellent type definitions for test data

The TestRecord and SimplifiedTestRecord types provide strong typing for the test cases, ensuring that test data structure matches the API requirements. This level of type safety helps catch errors during development rather than at runtime.


187-203: Well-structured parameterized tests for createKey

Using describe.for and test.for with objectEntries is an excellent approach for data-driven testing. The tests are concise and clearly validate that each property is correctly handled when creating keys.


205-221:

✅ Verification successful

Limited updateKey testing scope

The test only verifies name and description properties for updateKey, but doesn't explain why other properties aren't tested. This might not represent all valid update scenarios.

Consider adding a comment explaining why only these properties are tested, or expand the test to include other updateable properties. Also verify which properties can be updated through the API:


🏁 Script executed:

#!/bin/bash
# Check which properties are allowed in the PatchApiKey type definition
rg -A 10 "export\s+type\s+PatchApiKey" src/

Length of output: 133


updateKey tests cover all permitted fields

The PatchApiKey type only includes name and description (see src/types/keys.ts), so the existing tests already cover every updateable property. No changes needed.

Comment on lines +1 to +250
);
expect(updatedKey).toHaveProperty("name", "Product admin");
});
assert.isKeyView(keyView);

test(`${permission} key: delete a key`, async () => {
const client = await getClient(permission);
assertion(keyView[key as keyof typeof keyView], value);
},
);
});

const key = await client.createKey({
description: "Indexing Products API key",
actions: ["documents.add"],
indexes: ["products"],
expiresAt: new Date("2050-11-13T00:00:00Z"), // Test will fail in 2050
});
test(`${ms.getKeys.name}, ${ms.getKey.name} and ${ms.deleteKey.name} methods`, async () => {
const keyList = await ms.getKeys({
offset: 0,
limit: 10_000,
} satisfies Required<ListApiKeys>);

const deletedKey = await client.deleteKey(key.key);
for (const { uid, name } of keyList.results) {
const keyView = await ms.getKey(uid);

expect(deletedKey).toBeUndefined();
});
},
);
// avoid deleting default keys that might be used by other tests
if (name !== "Default Search API Key" && name !== "Default Admin API Key") {
await ms.deleteKey(uid);
}

describe.each([{ permission: "Search" }])(
"Test on keys with search key",
({ permission }) => {
test(`${permission} key: get keys denied`, async () => {
const client = await getClient(permission);
await expect(client.getKeys()).rejects.toHaveProperty(
"cause.code",
ErrorStatusCode.INVALID_API_KEY,
);
});
assert.isKeyView(keyView);
}

test(`${permission} key: create key denied`, async () => {
const client = await getClient(permission);
await expect(
client.createKey({
description: "Indexing Products API key",
actions: ["documents.add"],
indexes: ["products"],
expiresAt: null,
}),
).rejects.toHaveProperty("cause.code", ErrorStatusCode.INVALID_API_KEY);
});
},
);
assert.lengthOf(Object.keys(keyList), 4);
const { results, offset, limit, total } = keyList;

describe.each([{ permission: "No" }])(
"Test on keys with No key",
({ permission }) => {
test(`${permission} key: get keys denied`, async () => {
const client = await getClient(permission);
await expect(client.getKeys()).rejects.toHaveProperty(
"cause.code",
ErrorStatusCode.MISSING_AUTHORIZATION_HEADER,
);
});
for (const keyView of results) {
assert.isKeyView(keyView);
}

test(`${permission} key: create key denied`, async () => {
const client = await getClient(permission);
await expect(
client.createKey({
description: "Indexing Products API key",
actions: ["documents.add"],
indexes: ["products"],
expiresAt: null,
}),
).rejects.toHaveProperty(
"cause.code",
ErrorStatusCode.MISSING_AUTHORIZATION_HEADER,
);
});
},
);
assert.typeOf(offset, "number");
assert.typeOf(limit, "number");
assert.typeOf(total, "number");
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Error handling tests are missing

The test suite thoroughly tests the happy paths but lacks tests for error scenarios, such as invalid inputs or unauthorized access.

Consider adding tests for error cases like:

  1. Creating a key with invalid properties
  2. Updating a non-existent key
  3. Accessing a key with insufficient permissions
  4. Using invalid pagination parameters

Example structure:

test('createKey should fail with invalid properties', async () => {
  await assert.rejects(
    async () => ms.createKey({
      actions: ["invalid_action"],
      indexes: ["*"],
      expiresAt: null
    }),
    (err) => {
      assert.include(err.message, 'invalid_action');
      return true;
    }
  );
});
🤖 Prompt for AI Agents
In tests/keys.test.ts around lines 1 to 250, the test suite currently covers
only successful scenarios and lacks tests for error handling. Add new tests to
cover error cases such as creating a key with invalid properties, updating a
non-existent key, accessing a key without proper permissions, and using invalid
pagination parameters. For each, write tests that call the relevant method with
invalid inputs and assert that the promise rejects with an error containing
expected messages, using assert.rejects or equivalent to verify proper error
handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking-change The related changes are breaking for the users
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant