Skip to content

Commit

Permalink
@tus/file-store: fix list method of FileConfigstore (#490)
Browse files Browse the repository at this point in the history
  • Loading branch information
Murderlon authored Oct 18, 2023
1 parent 9d14d7d commit 1b6889f
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 11 deletions.
15 changes: 8 additions & 7 deletions packages/file-store/configstores/FileConfigstore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ export class FileConfigstore implements Configstore {

async list(): Promise<Array<string>> {
return this.queue.add(async () => {
const files = await fs.readdir(this.directory, {withFileTypes: true})
const promises = files
.filter((file) => file.isFile() && file.name.endsWith('.json'))
.map((file) => fs.readFile(path.resolve(file.path, file.name), 'utf8'))

return Promise.all(promises)
}) as Promise<string[]>
const files = await fs.readdir(this.directory)
const sorted = files.sort((a, b) => a.localeCompare(b))
const name = (file: string) => path.basename(file, '.json')
// To only return tus file IDs we check if the file has a corresponding JSON info file
return sorted.filter(
(file, idx) => idx < sorted.length - 1 && name(file) === name(sorted[idx + 1])
)
})
}

private resolve(key: string): string {
Expand Down
30 changes: 26 additions & 4 deletions packages/file-store/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@ import 'should'

import {strict as assert} from 'node:assert'
import fs from 'node:fs'
import fsProm from 'node:fs/promises'
import path from 'node:path'

import sinon from 'sinon'

import {FileStore} from './'
import {FileStore, FileConfigstore} from './'
import {Upload} from '@tus/server'

import * as shared from '../../test/stores.test'
import {MemoryConfigstore} from './configstores/MemoryConfigstore'

const fixturesPath = path.resolve('../', '../', 'test', 'fixtures')
const storePath = path.resolve('../', '../', 'test', 'output')

async function cleanup() {
await fsProm.rm(storePath, {recursive: true})
await fsProm.mkdir(storePath)
}

describe('FileStore', function () {
before(function () {
this.testFileSize = 960_244
Expand All @@ -28,13 +33,13 @@ describe('FileStore', function () {
sinon.spy(fs, 'mkdir')
this.datastore = new FileStore({
directory: this.storePath,
configstore: new MemoryConfigstore(),
})
})

this.afterEach(() => {
this.afterEach(async () => {
// @ts-expect-error ignore
fs.mkdir.restore()
await cleanup()
})

it('should create a directory for the files', function (done) {
Expand Down Expand Up @@ -89,9 +94,26 @@ describe('FileStore', function () {
})
})

describe('FileConfigstore', () => {
it('should ignore random files in directory when calling list()', async function () {
const store = new FileConfigstore(storePath)
const files = ['tus', 'tus.json', 'tu', 'tuss.json', 'random']
for (const file of files) {
await fsProm.writeFile(path.resolve(storePath, file), '')
}
const list = await store.list()

// list returns the amount of uploads.
// One upload consists of the file and the JSON info file.
// But from the list perspective that is only one upload.
assert.strictEqual(list.length, 1)
})
})

shared.shouldHaveStoreMethods()
shared.shouldCreateUploads()
shared.shouldRemoveUploads() // Termination extension
shared.shouldExpireUploads() // Expiration extension
shared.shouldWriteUploads()
shared.shouldHandleOffset()
shared.shouldDeclareUploadLength() // Creation-defer-length extension
Expand Down
26 changes: 26 additions & 0 deletions test/stores.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'should'
import {strict as assert} from 'node:assert'
import fs from 'node:fs'
import stream from 'node:stream'
import {setTimeout as promSetTimeout} from 'node:timers/promises'

import {Upload} from '@tus/server'

Expand Down Expand Up @@ -67,6 +68,31 @@ export const shouldCreateUploads = function () {
})
}

export const shouldExpireUploads = function () {
describe('expiration extension', () => {
it("should report 'expiration' extension", function () {
assert.equal(this.datastore.hasExtension('expiration'), true)
})

it('should expire upload', async function () {
const file = new Upload({
id: 'expiration-test',
size: this.testFileSize,
offset: 0,
metadata: {filename: 'world_domination_plan.pdf', is_confidential: null},
})
this.datastore.expirationPeriodInMilliseconds = 100
await this.datastore.create(file)
const readable = fs.createReadStream(this.testFilePath)
const offset = await this.datastore.write(readable, file.id, 0)
await promSetTimeout(100)
const n = await this.datastore.deleteExpired()
assert.equal(offset, this.testFileSize)
assert.equal(n, 1)
})
})
}

export const shouldRemoveUploads = function () {
const file = new Upload({id: 'remove-test', size: 1000, offset: 0})

Expand Down

0 comments on commit 1b6889f

Please sign in to comment.