Skip to main content
Glama

Karakeep MCP server

by karakeep-app
s3-store.test.ts5.88 kB
import { HeadObjectCommand, S3Client } from "@aws-sdk/client-s3"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { S3AssetStore } from "@karakeep/shared/assetdb"; import { assertAssetExists, cleanupTestBucket, createS3Store, createTestAssetData, createTestBucket, } from "./assetdb-utils"; describe("S3AssetStore - S3-Specific Behaviors", () => { let bucketName: string; let s3Client: S3Client; let store: S3AssetStore; beforeEach(async () => { bucketName = `test-bucket-${Math.random().toString(36).substring(7)}`; s3Client = await createTestBucket(bucketName); store = createS3Store(bucketName); }); afterEach(async () => { await cleanupTestBucket(s3Client, bucketName); }); describe("S3 Object Structure", () => { it("should store asset as single object with user-defined metadata", async () => { const testData = createTestAssetData(); await store.saveAsset({ userId: testData.userId, assetId: testData.assetId, asset: testData.content, metadata: testData.metadata, }); // Verify the object exists with the expected key structure const objectKey = `${testData.userId}/${testData.assetId}`; const headResponse = await s3Client.send( new HeadObjectCommand({ Bucket: bucketName, Key: objectKey, }), ); // Verify metadata is stored in S3 user-defined metadata expect(headResponse.Metadata).toBeDefined(); expect(headResponse.Metadata!["x-amz-meta-content-type"]).toBe( testData.metadata.contentType, ); expect(headResponse.Metadata!["x-amz-meta-file-name"]).toBe( testData.metadata.fileName || "", ); // Verify content type is set correctly on the S3 object expect(headResponse.ContentType).toBe(testData.metadata.contentType); }); it("should handle null fileName in metadata correctly", async () => { const testData = createTestAssetData({ metadata: { contentType: "text/html", fileName: null, }, }); await store.saveAsset({ userId: testData.userId, assetId: testData.assetId, asset: testData.content, metadata: testData.metadata, }); const objectKey = `${testData.userId}/${testData.assetId}`; const headResponse = await s3Client.send( new HeadObjectCommand({ Bucket: bucketName, Key: objectKey, }), ); // Verify null fileName are not stored in S3 metadata expect(headResponse.Metadata!["x-amz-meta-file-name"]).toBeUndefined(); // Verify reading back gives us null fileName const metadata = await store.readAssetMetadata({ userId: testData.userId, assetId: testData.assetId, }); expect(metadata.fileName).toBeNull(); }); }); describe("S3 Key Structure", () => { it("should use clean userId/assetId key structure", async () => { const userId = "user123"; const assetId = "asset456"; const testData = createTestAssetData({ userId, assetId }); await store.saveAsset({ userId: testData.userId, assetId: testData.assetId, asset: testData.content, metadata: testData.metadata, }); // Verify the exact key structure const expectedKey = `${userId}/${assetId}`; const headResponse = await s3Client.send( new HeadObjectCommand({ Bucket: bucketName, Key: expectedKey, }), ); expect(headResponse.ContentLength).toBe(testData.content.length); }); it("should handle special characters in user and asset IDs", async () => { const userId = "user/with/slashes"; const assetId = "asset-with-special-chars_123"; const testData = createTestAssetData({ userId, assetId }); await store.saveAsset({ userId: testData.userId, assetId: testData.assetId, asset: testData.content, metadata: testData.metadata, }); await assertAssetExists(store, testData.userId, testData.assetId); }); }); describe("S3 Eventual Consistency", () => { it("should handle immediate read after write (MinIO strong consistency)", async () => { const testData = createTestAssetData(); await store.saveAsset({ userId: testData.userId, assetId: testData.assetId, asset: testData.content, metadata: testData.metadata, }); // Immediately try to read - should work with MinIO's strong consistency const { asset, metadata } = await store.readAsset({ userId: testData.userId, assetId: testData.assetId, }); expect(asset).toEqual(testData.content); expect(metadata).toEqual(testData.metadata); }); }); describe("S3 Metadata Conversion", () => { it("should correctly convert between AssetMetadata and S3 metadata", async () => { const testCases = [ { contentType: "image/jpeg", fileName: "test-image.jpg", }, { contentType: "application/pdf", fileName: "document.pdf", }, { contentType: "text/html", fileName: null, }, ]; for (const metadata of testCases) { const testData = createTestAssetData({ metadata }); await store.saveAsset({ userId: testData.userId, assetId: testData.assetId, asset: testData.content, metadata: testData.metadata, }); // Verify metadata round-trip const retrievedMetadata = await store.readAssetMetadata({ userId: testData.userId, assetId: testData.assetId, }); expect(retrievedMetadata).toEqual(testData.metadata); } }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/karakeep-app/karakeep'

If you have feedback or need assistance with the MCP directory API, please join our Discord server