feat: backend implementation for media manager v2 (WIP - Undeployed)

This commit is contained in:
Ben Miller
2025-12-28 08:13:27 -07:00
parent a9cb63fd67
commit 6e1222cec9
11 changed files with 763 additions and 189 deletions

View File

@ -1,3 +1,4 @@
import { MediaService } from "./MediaService"
import { MockDriveService } from "./MockDriveService"
import { MockShopifyMediaService } from "./MockShopifyMediaService"
@ -12,8 +13,9 @@ class MockNetworkService implements INetworkService {
this.lastUrl = url
this.lastPayload = params.payload
return {
getResponseCode: () => 200
} as GoogleAppsScript.URL_Fetch.HTTPResponse
getResponseCode: () => 200,
getBlob: () => ({ getBytes: () => [], getContentType: () => "image/jpeg", setName: () => {} })
} as unknown as GoogleAppsScript.URL_Fetch.HTTPResponse
}
}
@ -28,28 +30,89 @@ describe("MediaService", () => {
driveService = new MockDriveService()
shopifyService = new MockShopifyMediaService()
networkService = new MockNetworkService()
config = { productPhotosFolderId: "root" } as Config // Mock config
config = { productPhotosFolderId: "root" } as Config
mediaService = new MediaService(driveService, shopifyService, networkService, config)
// Global Mocks
global.Utilities = {
base64Encode: (b) => "base64",
newBlob: (b, m, n) => ({
getBytes: () => b,
getContentType: () => m,
getName: () => n,
setName: () => {}
})
} as any
global.Drive = { Files: { get: () => ({ appProperties: {} }) } } as any
global.UrlFetchApp = networkService as unknown as GoogleAppsScript.URL_Fetch.UrlFetchApp
})
test("syncMediaForSku uploads files from Drive to Shopify", () => {
// Setup Drive State
test("getUnifiedMediaState should match files", () => {
const folder = driveService.getOrCreateFolder("SKU123", "root")
const blob1 = { getName: () => "01.jpg", getMimeType: () => "image/jpeg", getBytes: () => [] } as unknown as GoogleAppsScript.Base.Blob
const blob1 = { getName: () => "01.jpg", getMimeType: () => "image/jpeg", getBytes: () => [], getThumbnail: () => ({ getBytes: () => [] }) } as unknown as GoogleAppsScript.Base.Blob
driveService.saveFile(blob1, folder.getId())
// Run Sync
mediaService.syncMediaForSku("SKU123", "shopify_prod_id")
// Verify Network Call (Upload)
expect(networkService.lastUrl).toBe("https://mock-upload.shopify.com")
// Verify payload contained file
expect(networkService.lastPayload).toHaveProperty("file")
const state = mediaService.getUnifiedMediaState("SKU123", "pid")
expect(state).toHaveLength(1)
expect(state[0].filename).toBe("01.jpg")
})
test("syncMediaForSku does nothing if no files", () => {
mediaService.syncMediaForSku("SKU_EMPTY", "pid")
expect(networkService.lastUrl).toBe("")
test("processMediaChanges should handle deletions", () => {
const folder = driveService.getOrCreateFolder("SKU123", "root")
const blob1 = {
getName: () => "delete_me.jpg",
getId: () => "file_id_1",
getMimeType: () => "image/jpeg",
getBytes: () => [],
getThumbnail: () => ({ getBytes: () => [] })
} as unknown as GoogleAppsScript.Base.Blob
driveService.saveFile(blob1, folder.getId())
// Update Shopify Mock to return this media
shopifyService.getProductMedia = jest.fn().mockReturnValue([{
id: "gid://shopify/Media/media_1",
alt: "delete_me.jpg"
}])
// Update global Drive to return synced ID
global.Drive = { Files: { get: () => ({ appProperties: { shopify_media_id: "gid://shopify/Media/media_1" } }) } } as any
const finalState = []
const deleteSpy = jest.spyOn(shopifyService, 'productDeleteMedia')
const trashSpy = jest.spyOn(driveService, 'trashFile')
mediaService.processMediaChanges("SKU123", finalState, "pid")
expect(deleteSpy).toHaveBeenCalled()
expect(trashSpy).toHaveBeenCalled()
})
test("processMediaChanges should handle backfills (Shopify -> Drive)", () => {
// Current state: Empty Drive, 1 Shopify Media
shopifyService.getProductMedia = jest.fn().mockReturnValue([{
id: "gid://shopify/Media/media_2",
alt: "backfill.jpg",
preview: { image: { originalSrc: "http://shopify.com/img.jpg" } }
}])
// Final state: 1 item (the backfilled one)
const finalState = [{
id: "gid://shopify/Media/media_2",
filename: "backfill.jpg",
status: "synced"
}]
// Mock network fetch for download
jest.spyOn(networkService, 'fetch')
mediaService.processMediaChanges("SKU123", finalState, "pid")
// Should create file in Drive
const folder = driveService.getOrCreateFolder("SKU123", "root")
const files = driveService.getFiles(folder.getId())
expect(files).toHaveLength(1)
expect(files[0].getName()).toBe("backfill.jpg")
})
})