attachments.test.ts•6.1 kB
import { describe, it, expect, beforeAll } from 'vitest'
import { ShortcutClient } from '../../src/shortcutClient'
describe('Attachment functionality', () => {
let client: ShortcutClient
beforeAll(() => {
const apiToken = process.env.SHORTCUT_TOKEN
if (!apiToken) {
throw new Error('SHORTCUT_TOKEN not found in environment')
}
client = new ShortcutClient({
apiToken,
baseUrl: 'https://api.app.shortcut.com/api/v3',
})
})
describe('getStoryFiles', () => {
it('should retrieve files from a story', async () => {
// Find a story with attachments
const results = await client.searchStories('has:attachment')
if (results.stories.data.length > 0) {
const storyWithFiles = results.stories.data.find(s => s.files && s.files.length > 0)
if (storyWithFiles) {
const files = await client.getStoryFiles(storyWithFiles.id)
expect(files).toBeDefined()
expect(Array.isArray(files)).toBe(true)
if (files.length > 0) {
const file = files[0]
expect(file).toHaveProperty('id')
expect(file).toHaveProperty('name')
expect(file).toHaveProperty('url')
expect(file).toHaveProperty('content_type')
expect(file).toHaveProperty('size')
}
}
}
})
})
describe('downloadFile', () => {
it('should download file content', async () => {
// Find a story with a small text file
const results = await client.searchStories('has:attachment')
if (results.stories.data.length > 0) {
const storyWithFiles = results.stories.data.find(s =>
s.files && s.files.some(f =>
f.content_type?.includes('text') ||
f.name?.endsWith('.txt') ||
f.name?.endsWith('.csv')
)
)
if (storyWithFiles && storyWithFiles.files) {
const textFile = storyWithFiles.files.find(f =>
f.content_type?.includes('text') ||
f.name?.endsWith('.txt') ||
f.name?.endsWith('.csv')
)
if (textFile && textFile.url && textFile.size < 1000000) { // Less than 1MB
const { content, contentType } = await client.downloadFile(textFile.url)
expect(content).toBeDefined()
expect(content).toBeInstanceOf(Buffer)
expect(content.length).toBeGreaterThan(0)
expect(contentType).toBeDefined()
}
}
}
})
})
describe('downloadFileAsText', () => {
it('should download and decode text files correctly', async () => {
// Find a story with a CSV or text file
const results = await client.searchStories('has:attachment')
if (results.stories.data.length > 0) {
const storyWithTextFile = results.stories.data.find(s =>
s.files && s.files.some(f =>
f.content_type === 'text/csv' ||
f.content_type === 'text/plain' ||
f.name?.endsWith('.csv') ||
f.name?.endsWith('.txt')
)
)
if (storyWithTextFile && storyWithTextFile.files) {
const textFile = storyWithTextFile.files.find(f =>
f.content_type === 'text/csv' ||
f.content_type === 'text/plain' ||
f.name?.endsWith('.csv') ||
f.name?.endsWith('.txt')
)
if (textFile && textFile.url && textFile.size < 1000000) { // Less than 1MB
const content = await client.downloadFileAsText(textFile.url)
expect(content).toBeDefined()
expect(typeof content).toBe('string')
expect(content.length).toBeGreaterThan(0)
// Should not be base64 for text files
expect(content).not.toMatch(/^[A-Za-z0-9+/]+=*$/)
}
}
}
})
it('should return base64 for binary files', async () => {
// Find a story with an image
const results = await client.searchStories('has:attachment')
if (results.stories.data.length > 0) {
const storyWithImage = results.stories.data.find(s =>
s.files && s.files.some(f => f.content_type?.startsWith('image/'))
)
if (storyWithImage && storyWithImage.files) {
const imageFile = storyWithImage.files.find(f =>
f.content_type?.startsWith('image/')
)
if (imageFile && imageFile.url && imageFile.size < 100000) { // Less than 100KB
const content = await client.downloadFileAsText(imageFile.url)
expect(content).toBeDefined()
expect(typeof content).toBe('string')
// Should be valid base64
expect(() => Buffer.from(content, 'base64')).not.toThrow()
}
}
}
})
})
})