@timothy-spaceman/multitrack-vcs
Version:
Version Control System for musicians
371 lines (304 loc) • 11.1 kB
text/typescript
import path from "node:path"
import * as process from "process"
import {CONTENT_DIR, FsStorageProvider, Project, PROJECT_DIR, VirtualStorageProvider} from "../src/index.js"
let uuidCounter = 0
jest.mock("crypto", () => {
const actualCrypto = jest.requireActual("crypto")
return {
...actualCrypto,
randomUUID: jest.fn(() => `test-uuid-${uuidCounter++}`),
}
})
const tmp = "tests-tmp"
const sp = new FsStorageProvider()
VirtualStorageProvider.separator = "\\"
const getProjectFile = async () => {
const projectFile = await sp.readFile(path.join(PROJECT_DIR, "project.json"))
const fileContent = (await projectFile.readData()).toString()
return JSON.parse(fileContent)
}
const getFileContent = async (filePath: string) => {
const file = await sp.readFile(filePath)
const fileData = await file.readData()
return fileData.toString()
}
const shiftTime = function (ms: number) {
const d = new Date()
d.setMilliseconds(ms)
jest.setSystemTime(d)
}
const clearTmp = async () => {
const files = await sp.readDir(".")
for (const file of files) {
await sp.deleteFileOrDir(file)
}
}
beforeAll(async () => {
jest
.useFakeTimers()
.setSystemTime(new Date("2025-01-01T00:00:00.000Z"))
if (path.basename(process.cwd()) != tmp) {
if (!await sp.exists(tmp)) {
await sp.createDir(tmp)
}
process.chdir(tmp)
}
await clearTmp()
})
describe("Commits", () => {
let project: Project
beforeAll(async () => {
uuidCounter = 1
project = await Project.create(
sp,
".",
"JEST",
"JEST_PROJECT",
)
})
afterAll(async () => {
await clearTmp()
})
test("Initial", async () => {
const commitId = "test-uuid-4"
const contentPath = "test-uuid-3"
const itemId = "test-uuid-2"
await sp.createFile("file1.txt", Buffer.from("First line ever"))
await project.commit(
await sp.readDirDeep(".", [".mvcs"]),
"JEST",
"Initial Commit",
"First commit in this project"
)
await project.save()
expect(project.currentCommitId).toBe(commitId)
expect(project.commits.get(commitId)).toMatchObject({
"id": commitId,
"children": [],
"authorId": "JEST",
"title": "Initial Commit",
"description": "First commit in this project",
"date": "2025-01-01T00:00:00.000Z",
"changes": [
{
"to": itemId
}
]
})
expect(project.items.get(itemId)).toMatchObject({
"id": itemId,
"content": contentPath,
"path": "file1.txt"
})
expect(await getFileContent(path.join(PROJECT_DIR, CONTENT_DIR, contentPath))).toBe("First line ever")
shiftTime(5000)
})
test("Changes", async () => {
const previousItemId = "test-uuid-2"
const newItemId = "test-uuid-5"
const newContentId = "test-uuid-6"
const newCommitId = "test-uuid-7"
const parentCommitId = "test-uuid-4"
const file = await sp.readFile("file1.txt")
await file.writeData(Buffer.from("First line ever\nSecond line"))
await project.commit(
await sp.readDirDeep(".", [".mvcs"]),
"JEST",
"Modified file1.txt",
)
await project.save()
expect(project.currentCommitId).toBe(newCommitId)
expect(project.commits.get(newCommitId)).toMatchObject({
id: newCommitId,
parent: parentCommitId,
children: [],
authorId: "JEST",
title: "Modified file1.txt",
description: "",
date: "2025-01-01T00:00:05.000Z",
changes: [{from: previousItemId, to: newItemId}]
})
expect(project.items.get(newItemId)).toMatchObject({
id: newItemId,
content: newContentId,
path: "file1.txt"
})
expect(await getFileContent(path.join(PROJECT_DIR, CONTENT_DIR, newContentId)))
.toBe("First line ever\nSecond line")
shiftTime(5000)
})
test("Subdirs", async () => {
const newItemId = "test-uuid-9"
const newContentId = "test-uuid-10"
const newCommitId = "test-uuid-11"
const parentCommitId = "test-uuid-7"
await sp.createFile("subdir1/file2.txt", Buffer.from("First file in subdir1"))
await project.commit(
await sp.readDirDeep(".", [".mvcs"]),
"JEST",
"Added subdir1"
)
await project.save()
expect(project.currentCommitId).toBe(newCommitId)
expect(project.commits.get(newCommitId)).toMatchObject({
id: newCommitId,
parent: parentCommitId,
children: [],
authorId: "JEST",
title: "Added subdir1",
description: "",
date: "2025-01-01T00:00:10.000Z",
changes: [{to: newItemId}]
})
expect(project.items.get(newItemId)).toMatchObject({
id: newItemId,
content: newContentId,
path: "subdir1\\file2.txt"
})
expect(await getFileContent(path.join(PROJECT_DIR, CONTENT_DIR, newContentId)))
.toBe("First file in subdir1")
shiftTime(5000)
})
test("Renamed/Moved Files", async () => {
const oldItemId = "test-uuid-5"
const newItemId = "test-uuid-14"
const newContentId = "test-uuid-6"
const newCommitId = "test-uuid-15"
const parentCommitId = "test-uuid-11"
const prevFilesState = await sp.readDirDeep(".", [".mvcs"])
await sp.moveFile("file1.txt", "subdir1/file1.txt")
const newFilesState = await sp.readDirDeep(".", [".mvcs"])
await project.commit(
[...prevFilesState, ...newFilesState],
"JEST",
"Moved file1 into subdir1"
)
await project.save()
expect(project.currentCommitId).toBe(newCommitId)
expect(project.commits.get(newCommitId)).toMatchObject({
id: newCommitId,
parent: parentCommitId,
children: [],
authorId: "JEST",
title: "Moved file1 into subdir1",
description: "",
date: "2025-01-01T00:00:15.000Z",
changes: [{from: oldItemId}, {to: newItemId}]
})
expect(project.items.get(newItemId)).toMatchObject({
id: newItemId,
content: newContentId,
path: "subdir1\\file1.txt"
})
shiftTime(5000)
})
test("Copies", async () => {
const newItemId = "test-uuid-16"
const newContentId = "test-uuid-6"
const newCommitId = "test-uuid-19"
const parentCommitId = "test-uuid-15"
await sp.copyFile("subdir1/file1.txt", "file1-copy.txt")
await project.commit(
await sp.readDirDeep(".", [".mvcs"]),
"JEST",
"Copied file1"
)
await project.save()
expect(project.currentCommitId).toBe(newCommitId)
expect(project.commits.get(newCommitId)).toMatchObject({
id: newCommitId,
parent: parentCommitId,
children: [],
authorId: "JEST",
title: "Copied file1",
description: "",
date: "2025-01-01T00:00:20.000Z",
changes: [{to: newItemId}]
})
expect(project.items.get(newItemId)).toMatchObject({
id: newItemId,
content: newContentId,
path: "file1-copy.txt"
})
shiftTime(5000)
})
test("Deletions", async () => {
const deletedItem1 = "test-uuid-9"
const deletedItem2 = "test-uuid-14"
const newCommitId = "test-uuid-21"
const parentCommitId = "test-uuid-19"
const filesToCommit = await sp.readDirDeep(".", [".mvcs"])
await sp.deleteFileOrDir("subdir1")
await project.commit(
filesToCommit,
"JEST",
"Removed subdir1"
)
await project.save()
expect(project.currentCommitId).toBe(newCommitId)
expect(project.commits.get(newCommitId)).toMatchObject({
id: newCommitId,
parent: parentCommitId,
children: [],
authorId: "JEST",
title: "Removed subdir1",
description: "",
date: "2025-01-01T00:00:25.000Z",
changes: [
{from: deletedItem1},
{from: deletedItem2}
]
})
})
test("Checkout", async () => {
interface CheckoutTestCase {
id: string,
filesList: string[],
filesToCheck: {
[fileName: string]: string
},
}
const verifyCheckout = async ({id, filesList, filesToCheck}: CheckoutTestCase) => {
await project.checkout(id)
expect(await sp.readDirDeep(".")).toEqual(expect.arrayContaining(filesList))
for (const [filePath, content] of Object.entries(filesToCheck)) {
expect(await getFileContent(filePath)).toBe(content)
}
}
const testCases: CheckoutTestCase[] = [
{
id: "test-uuid-4",
filesList: ["file1.txt", ".mvcs"],
filesToCheck: {
"file1.txt": "First line ever",
}
},
{
id: "test-uuid-11",
filesList: ["file1.txt", "subdir1", "subdir1\\file2.txt", ".mvcs"],
filesToCheck: {
"file1.txt": "First line ever\nSecond line",
"subdir1\\file2.txt": "First file in subdir1"
}
},
{
id: "test-uuid-21",
filesList: ["file1-copy.txt", "subdir1", ".mvcs"],
filesToCheck: {
"file1-copy.txt": "First line ever\nSecond line"
}
},
{
id: "test-uuid-15",
filesList: ["subdir1", "subdir1\\file1.txt", "subdir1\\file2.txt", ".mvcs"],
filesToCheck: {
"subdir1\\file1.txt": "First line ever\nSecond line",
"subdir1\\file2.txt": "First file in subdir1"
}
}
]
for (const testCase of testCases) {
await verifyCheckout(testCase)
}
})
})