diff --git a/lib/repository.js b/lib/repository.js index 3908ff5..aacf7c9 100644 --- a/lib/repository.js +++ b/lib/repository.js @@ -1,64 +1,68 @@ -const fs = require('fs-extra'); -const path = require('path'); -const pairtree = require('pairtree') -const OcflObject = require('./ocflObject'); -const uuidv4 = require("uuidv4") +const fs = require("fs-extra"); +const path = require("path"); +const pairtree = require("pairtree"); +const OcflObject = require("./ocflObject"); +const uuidv4 = require("uuidv4"); +const { compact } = require("lodash"); const DEPOSIT_DIR = "deposit"; class Repository { constructor() { - this.ocflVersion = '1.0'; + this.ocflVersion = "1.0"; // For now we only put things on pairtree paths this.objectIdToPath = pairtree.path; } async create(path) { if (this.path) { - throw new Error("This repository has already been initialized.") + throw new Error("This repository has already been initialized."); } this.path = path; // Sets up an empty repository minus the requisite /v1 directory //checks if the dir exists else dies const stats = await fs.stat(this.path); - if (await fs.pathExists(this.path) && stats.isDirectory()) { + if ((await fs.pathExists(this.path)) && stats.isDirectory()) { const readDir = await fs.readdir(this.path); - if (readDir.length <= 0) { // empty so initialise a repo here - const generateNamaste = await this.generateNamaste(this.path, this.ocflVersion); + if (readDir.length <= 0) { + // empty so initialise a repo here + const generateNamaste = await this.generateNamaste( + this.path, + this.ocflVersion + ); + } else { + throw new Error( + "can't initialise a repository as there are already files." + ); } - else { - throw new Error('can\'t initialise a repository as there are already files.' ) - } - } - else { + } else { //else if dir doesn't exist it dies - throw new Error('directory does not exist'); + throw new Error("directory does not exist"); } } async load(path) { // Connects to an existing repo residing at if (this.path) { - throw new Error("This repository has already been initialized.") + throw new Error("This repository has already been initialized."); } this.path = path; const stats = await fs.stat(this.path); - if (await fs.pathExists(this.path) && stats.isDirectory()) { - const version = await this.isContentRoot(this.path); - if (!version) { - throw new Error('Not an OCFL repository.'); - } - } - else { - throw new Error('Directory does not exist.'); + if ((await fs.pathExists(this.path)) && stats.isDirectory()) { + const version = await this.isContentRoot(this.path); + if (!version) { + throw new Error("Not an OCFL repository."); + } + } else { + throw new Error("Directory does not exist."); } -} + } async export(id, exportPath, options) { // Exports a directory to exportPath // TODO consider moving this to the object class - if (!await fs.pathExists(exportPath)) { + if (!(await fs.pathExists(exportPath))) { throw new Error("Can't export as the directory does not exist."); } @@ -66,7 +70,8 @@ class Repository { if (stats.isDirectory()) { const readDir = await fs.readdir(exportPath); - if (readDir.length > 0) { // NOt empty so complain + if (readDir.length > 0) { + // NOt empty so complain throw new Error("Can't export as the directory has stuff in it."); } } else { @@ -75,7 +80,6 @@ class Repository { // TODO handle versions look in options.version - // TODO handle options link // TODO make a new method objectIdToAbsolute path const objectPath = path.join(this.path, this.objectIdToPath(id)); @@ -87,14 +91,17 @@ class Repository { ver = options.version; } if (!inv.versions[ver]) { - throw new Error("Can't export a version that doesn't exist.") + throw new Error("Can't export a version that doesn't exist."); } const state = inv.versions[ver].state; for (const hash of Object.keys(state)) { // Hashes point to arrays of paths for (const f of state[hash]) { const fileExportTo = path.join(exportPath, f); - const fileExportFrom = path.join(objToExport.path, inv.manifest[hash][0]); + const fileExportFrom = path.join( + objToExport.path, + inv.manifest[hash][0] + ); const copied = await fs.copy(fileExportFrom, fileExportTo); } } @@ -107,7 +114,7 @@ class Repository { } incrementVersion(ver) { - return "v" + (parseInt(ver.replace("v", "")) + 1) + return "v" + (parseInt(ver.replace("v", "")) + 1); } async findObjects(dir, objects) { @@ -116,112 +123,133 @@ class Repository { for (let d of dirs) { const potential_dir = path.join(dir, d); const stats = await fs.stat(potential_dir); - if (d != DEPOSIT_DIR && await fs.pathExists(potential_dir) && stats.isDirectory()) { - + if ( + d != DEPOSIT_DIR && + (await fs.pathExists(potential_dir)) && + stats.isDirectory() + ) { // Looks like an an object - const object = new OcflObject() - const objectNamastePath = path.join(potential_dir, "0=" + object.nameVersion(this.ocflVersion)); + const object = new OcflObject(); + const objectNamastePath = path.join( + potential_dir, + "0=" + object.nameVersion(this.ocflVersion) + ); if (await fs.exists(objectNamastePath)) { - const o = await object.load(potential_dir) - objects.push(object) - } - else { + const o = await object.load(potential_dir); + objects.push(object); + } else { objects.concat(await this.findObjects(potential_dir, objects)); } } - } return objects; } - async makeDepositPath(id) { // Make sure we have a working directory const depositPath = path.join(this.path, DEPOSIT_DIR); - if (!await fs.pathExists(depositPath)) { + if (!(await fs.pathExists(depositPath))) { const depositDir = await fs.mkdir(depositPath); } // Make a temp directory under STORAGE_ROOT/deposit/ // Use pairtree to escape path but get rid of long path - const objectDepositPath = path.join(this.path, "deposit", this.objectIdToPath(id).replace(/\//g, "")); + const objectDepositPath = path.join( + this.path, + "deposit", + this.objectIdToPath(id).replace(/\//g, "") + ); if (await fs.pathExists(objectDepositPath)) { - throw new Error('There is already an object with this ID being deposited or left behind after a crash. Cannot proceed.') + throw new Error( + "There is already an object with this ID being deposited or left behind after a crash. Cannot proceed." + ); } await fs.mkdirp(objectDepositPath); return objectDepositPath; } - async importNewObjectDir(id, sourceDir) { - // Add an object to the repository given a source directory // id = an optional id String // dir = a directory somewhere on disk - return await this.createNewObjectContent(id, async (targetDir) => { + return await this.createNewObjectContent(id, async targetDir => { await fs.copy(sourceDir, targetDir); }); - - } - - - - + } async createNewObjectContent(id, writeContent) { - // Add an object to the repository with a callback that provides the content. // This is called by importNewObject under the hood - // writeContent = a callback that takes one argument, the directory to + // writeContent = a callback that takes one argument, the directory to // which content is to be written (in the repo's deposit directory) // id = an optional id String - // Make a temp random ID used for deposit - + // If no ID supplied use the random one - gets returned later // TODO: Make this a URI - if (!id) { id = uuidv4() }; + if (!id) { + id = uuidv4(); + } + const object = new OcflObject(); + const objectRepoPath = path.join(this.path, this.objectIdToPath(id)); + // Check that no parent path is an OCFL object + if (await this.isChildOfAnotherObject({ id })) { + throw new Error( + `A parent of this path seems to be an OCFL object and that's not allowed` + ); + } + + // ok - let's get creating const objectDepositPath = await this.makeDepositPath(id); // Make a temp object in the /deposit dir in our repo - const object = new OcflObject(); const oi = await object.create(objectDepositPath); // Add content by initialising object with the calllback const initialized = await object.addContent(id, writeContent); - const objectRepoPath = path.join(this.path, this.objectIdToPath(id)); // Move (not copy) the new object to the repository - if (!await fs.pathExists(objectRepoPath)) { - var subPath = objectRepoPath - do { - subPath = path.join(subPath, ".."); - if (await object.isObject(subPath)) { - throw new Error("Cannot make an object. There is already an object in a higher level directory.") - } - } while (subPath.startsWith(this.path)); + if (!(await fs.pathExists(objectRepoPath))) { await object.removeEmptyDirectories(); await fs.move(objectDepositPath, objectRepoPath); } else { - // Directory exists, it might be our object but it might be on a path of an existing one (which is bad) - if (!await object.isObject(objectRepoPath)) { - throw new Error("There is no object here but the path already exists is this ID a subset of a longer one?") - } // Merge object const added = await this.mergeObjectWith(object, objectRepoPath); + await fs.removeSync(objectDepositPath); } const newObj = new OcflObject(); await newObj.load(objectRepoPath); return newObj; } - - + async isChildOfAnotherObject({ id, aPath }) { + if (id) aPath = pairtree.path(id); + + // this path cannot be the child of another object + // we can determine that by walking back up the path and looking + // for an "0=" + this.nameVersion(this.ocflVersion) file + const pathComponents = compact(aPath.split("/")); + + // ditch the final path element - we're only checking parents + // so by definition, the final path element is the one we're on + pathComponents.pop(); + let parentIsOcflObject = []; + aPath = this.path; + for (let p of pathComponents) { + aPath = path.join(aPath, "/", p); + const theFile = path.join( + aPath, + "0=" + this.nameVersion(this.ocflVersion) + ); + parentIsOcflObject.push(await fs.pathExists(theFile)); + } + return parentIsOcflObject.includes(true); + } async mergeObjectWith(newObject, prevObjectPath) { // Merge an object that's being submitted with one from the repositopry @@ -234,10 +262,10 @@ class Repository { const prevInventory = await prevObject.getInventory(); const newInventory = await newObject.getInventory(); - // Get latest state + // Get latest state const prevVersion = prevInventory.head; - const prevState = prevInventory.versions[prevVersion].state - const newState = newInventory.versions["v1"].state + const prevState = prevInventory.versions[prevVersion].state; + const newState = newInventory.versions["v1"].state; // Check whether this is just the same thing exactly as the last one if (Object.keys(newState).length === Object.keys(prevState).length) { @@ -257,11 +285,14 @@ class Repository { } // Increment version number from existing object - const newVersion = this.incrementVersion(prevVersion) + const newVersion = this.incrementVersion(prevVersion); // Move our v1 stuff to the new version number (we may delete some of it) - const moved = await fs.move(path.join(newObject.path, "v1"), path.join(newObject.path, newVersion)); - const newVersionPath = path.join(newObject.path, newVersion) + const moved = await fs.move( + path.join(newObject.path, "v1"), + path.join(newObject.path, newVersion) + ); + const newVersionPath = path.join(newObject.path, newVersion); // Go thru latest state one hash at a time for (let hash of Object.keys(newState)) { @@ -281,38 +312,43 @@ class Repository { // Addition: Newly added files appear as new entries in the state block of // the new version. The file should be stored and an entry for the new // content must be made in the manifest block of the object's inventory. - prevInventory.manifest[hash] = [path.join(newVersion, "content", file)]; + prevInventory.manifest[hash] = [ + path.join(newVersion, "content", file) + ]; // now we have at least one copy - subsequent copies in incoming objects can be deleted - } - else { - // We have a copy of this file so delete the physical file + } else { + // We have a copy of this file so delete the physical file // the file we have could be inhereted or re-instated const filePath = path.join(newVersionPath, "content", file); const del = await fs.remove(filePath); } - } } // Spec says: // Deletion: Files deleted from the previous version are simply removed // from the state block of the new version // BUT: We never added them so nothing to do! - prevInventory.versions[newVersion] = newInventory.versions["v1"] // As updated + prevInventory.versions[newVersion] = newInventory.versions["v1"]; // As updated prevInventory.head = newVersion; // Copy in the new version dir - const invs = await newObject.writeInventories(prevInventory, newVersion) + const invs = await newObject.writeInventories(prevInventory, newVersion); const rm = await newObject.removeEmptyDirectories(); - const vmoved = await fs.move(newVersionPath, path.join(prevObject.path, newVersion)); - const invs_new = await prevObject.writeInventories(prevInventory, newVersion); + const vmoved = await fs.move( + newVersionPath, + path.join(prevObject.path, newVersion) + ); + const invs_new = await prevObject.writeInventories( + prevInventory, + newVersion + ); // Clean up temp deposit dir const rm1 = await fs.remove(newObject.path); - } async isContentRoot(aPath) { // 0=ocfl_1.0 // looks at path and see if the content of the file is - const namastePath = path.join(aPath, "0=" + this.nameVersion(this.ocflVersion)); + const namastePath = path.join(aPath, `0=ocfl_${this.ocflVersion}`); return await fs.pathExists(namastePath); } async containsContentRoot(aPath) { @@ -320,13 +356,13 @@ class Repository { } nameVersion(version) { - return 'ocfl_' + version; + return `ocfl_object_${version}`; } async generateNamaste(aPath, version) { - const fileName = '0=' + this.nameVersion(version); + const fileName = `0=ocfl_${version}`; const thePath = path.join(aPath, fileName); - const writeFile = await fs.writeFile(thePath, this.nameVersion(version)); + const writeFile = await fs.writeFile(thePath, version); } } diff --git a/test/repository.spec.js b/test/repository.spec.js index 98ef89f..455149f 100644 --- a/test/repository.spec.js +++ b/test/repository.spec.js @@ -1,19 +1,18 @@ -const assert = require('assert'); -const path = require('path'); -const fs = require('fs-extra'); -const uuidv4 = require('uuidv4'); -const pairtree = require('pairtree'); -const hasha = require('hasha'); -const Repository = require('../lib/repository'); -const OcflObject = require('../lib/ocflObject'); +const assert = require("assert"); +const path = require("path"); +const fs = require("fs-extra"); +const uuidv4 = require("uuidv4"); +const pairtree = require("pairtree"); +const hasha = require("hasha"); +const Repository = require("../lib/repository"); +const OcflObject = require("../lib/ocflObject"); -const chai = require('chai'); +const chai = require("chai"); const expect = chai.expect; -chai.use(require('chai-fs')); - -const DIGEST_ALGORITHM = 'sha512'; +chai.use(require("chai-fs")); +const DIGEST_ALGORITHM = "sha512"; function createDirectory(aPath) { if (!fs.existsSync(aPath)) { @@ -22,11 +21,10 @@ function createDirectory(aPath) { } // Path constants -const repositoryPath = path.join(process.cwd(), './test-data/ocfl1'); -const sourcePath1 = path.join(process.cwd(), './test-data/ocfl-object1-source'); +const repositoryPath = path.join(process.cwd(), "./test-data/ocfl1"); +const sourcePath1 = path.join(process.cwd(), "./test-data/ocfl-object1-source"); const sourcePath1_additional_files = sourcePath1 + "_additional_files"; - async function createTestRepo() { fs.removeSync(repositoryPath); createDirectory(repositoryPath); @@ -35,93 +33,96 @@ async function createTestRepo() { return repository; } -describe('repository initialisation', function () { - - it('should not initialise previous created or loaded repositories', async function () { - const repository = await createTestRepo(); - try { - const init = await repository.create(repositoryPath); - } catch (e) { - assert.strictEqual(e.message, 'This repository has already been initialized.'); - } - }); - - it('should not initialise directories with files', async function () { - const repository = new Repository(); +describe("repository initialisation", function() { + it("should not initialise previous created or loaded repositories", async function() { + const repository = await createTestRepo(); + try { + const init = await repository.create(repositoryPath); + } catch (e) { + assert.strictEqual( + e.message, + "This repository has already been initialized." + ); + } + }); - try { - const init = await repository.create("."); - } catch (e) { - assert.strictEqual(e.message, "can't initialise a repository as there are already files."); - } - }); + it("should not initialise directories with files", async function() { + const repository = new Repository(); + try { + const init = await repository.create("."); + } catch (e) { + assert.strictEqual( + e.message, + "can't initialise a repository as there are already files." + ); + } + }); - it('Should not let you load twice', async function () { + it("Should not let you load twice", async function() { const repository = new Repository(); try { const new_id = await repository.load(repositoryPath); + } catch (e) { + assert.strictEqual( + e.message, + "This repository has already been initialized." + ); } - catch (e) { - assert.strictEqual(e.message, 'This repository has already been initialized.'); - } - }); - }); -describe('No directory to create a repo in', function () { - const repoPath = path.join(process.cwd(), './test-data/ocflX'); +describe("No directory to create a repo in", function() { + const repoPath = path.join(process.cwd(), "./test-data/ocflX"); const repository = new Repository(); - it('should test directory', async function f() { + it("should test directory", async function f() { try { const init = await repository.create(repoPath); } catch (e) { - assert.strictEqual(e.code, 'ENOENT') + assert.strictEqual(e.code, "ENOENT"); } - }); - }); -describe('Successful repository creation', function () { +describe("Successful repository creation", function() { const ocflVersion = "1.0"; - it('should test content root', async function () { + it("should test content root", async function() { const repository = await createTestRepo(); assert.strictEqual(repository.ocflVersion, ocflVersion); }); - it('repo path is set', async function () { + it("repo path is set", async function() { const repository = await createTestRepo(); assert.strictEqual(repository.path, repositoryPath); }); - it('should have a namaste file', async function () { + it("should have a namaste file", async function() { const repository = await createTestRepo(); - assert.strictEqual(fs.existsSync(path.join(repositoryPath, '0=ocfl_' + ocflVersion)), true); + assert.strictEqual( + fs.existsSync(path.join(repositoryPath, "0=ocfl_" + ocflVersion)), + true + ); }); const repository2 = new Repository(); - it('should initialise in a directory with an existing namaste file', async function () { + it("should initialise in a directory with an existing namaste file", async function() { const init = await repository2.load(repositoryPath); - assert.strictEqual(repository2.ocflVersion, ocflVersion) + assert.strictEqual(repository2.ocflVersion, ocflVersion); }); - - }); -describe('Adding objects from directories', function () { - - - const repeatedFileHash = "31bca02094eb78126a517b206a88c73cfa9ec6f704c7030d18212cace820f025f00bf0ea68dbf3f3a5436ca63b53bf7bf80ad8d5de7d8359d0b7fed9dbc3ab99"; - const file1Hash = "4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed6fa46510a"; - const sepiaPicHash = "577b1610764f4d9d05e82b5efe9b39214806e4c29249b59634699101a2a4679317388e78f7b40134aba8371cc684a9b422a7032d34a6785201051e484805bd59"; - const sepiaPicPath = "v1/content/sample/pics/sepia_fence.jpg" - const sepiaPicLogicalPath = "sample/pics/sepia_fence.jpg" - - - it('should make up an ID if you add content', async function () { +describe("Adding objects from directories", function() { + const repeatedFileHash = + "31bca02094eb78126a517b206a88c73cfa9ec6f704c7030d18212cace820f025f00bf0ea68dbf3f3a5436ca63b53bf7bf80ad8d5de7d8359d0b7fed9dbc3ab99"; + const file1Hash = + "4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed6fa46510a"; + const sepiaPicHash = + "577b1610764f4d9d05e82b5efe9b39214806e4c29249b59634699101a2a4679317388e78f7b40134aba8371cc684a9b422a7032d34a6785201051e484805bd59"; + const sepiaPicPath = "v1/content/sample/pics/sepia_fence.jpg"; + const sepiaPicLogicalPath = "sample/pics/sepia_fence.jpg"; + + it("should make up an ID if you add content", async function() { const repository = await createTestRepo(); const obj = await repository.importNewObjectDir(null, sourcePath1); const inv = await obj.getInventory(); @@ -129,22 +130,31 @@ describe('Adding objects from directories', function () { // We got a UUID as an an ID assert.strictEqual(new_id.length, 36); // Check that the object is there - const objectPath = path.join(repositoryPath, new_id.replace(/(..)/g, "$1/")); + const objectPath = path.join( + repositoryPath, + new_id.replace(/(..)/g, "$1/") + ); assert.strictEqual(fs.existsSync(objectPath), true); }); - it('should use your id for a new object if you give it one', async function () { + it("should use your id for a new object if you give it one", async function() { const repository = await createTestRepo(); - const obj = await repository.importNewObjectDir("some_other_id", sourcePath1); + const obj = await repository.importNewObjectDir( + "some_other_id", + sourcePath1 + ); // We got a UUID as an an ID - const inv = await (obj.getInventory()); + const inv = await obj.getInventory(); assert.strictEqual(inv.id, "some_other_id"); // Check that the object is there - const objectPath = path.join(repositoryPath, inv.id.replace(/(..)/g, "$1/")); + const objectPath = path.join( + repositoryPath, + inv.id.replace(/(..)/g, "$1/") + ); assert.strictEqual(fs.existsSync(objectPath), true); }); - it('should create a deposit directory in the repository path', async function () { + it("should create a deposit directory in the repository path", async function() { const repository = await createTestRepo(); const id = uuidv4(); const idpath = repository.objectIdToPath(id).replace(/\//g, ""); @@ -154,35 +164,39 @@ describe('Adding objects from directories', function () { expect(gpath).to.be.a.directory(`Created ${gpath}`).and.empty; }); - - it('should refuse to make an object if there is a failed attempt in the deposit dir', async function () { + it("should refuse to make an object if there is a failed attempt in the deposit dir", async function() { const repository = await createTestRepo(); try { - const depositDir = await fs.mkdirp(path.join(repositoryPath, "deposit", "some_id")); - const new_id = await repository.importNewObjectDir("some_id", sourcePath1); - } - catch (e) { - assert.strictEqual(e.message, 'There is already an object with this ID being deposited or left behind after a crash. Cannot proceed.'); + const depositDir = await fs.mkdirp( + path.join(repositoryPath, "deposit", "some_id") + ); + const new_id = await repository.importNewObjectDir( + "some_id", + sourcePath1 + ); + } catch (e) { + assert.strictEqual( + e.message, + "There is already an object with this ID being deposited or left behind after a crash. Cannot proceed." + ); } - }); - it('Should now have three objects in it', async function () { + it("Should now have three objects in it", async function() { const repository = await createTestRepo(); const obj1 = await repository.importNewObjectDir("1", sourcePath1); const obj2 = await repository.importNewObjectDir("2", sourcePath1); const obj3 = await repository.importNewObjectDir("3", sourcePath1); const objects = await repository.objects(); - assert.strictEqual(objects.length, 3) + assert.strictEqual(objects.length, 3); //TODO - Check Object IDs - }); - // TODO: break this into smaller it()s and fix the 211 magic number bug + // TODO: break this into smaller it()s and fix the 211 magic number bug - it('should handle file additions and export', async function () { + it("should handle file additions and export", async function() { // TODO this depends on tests above running - fix that! const repository = new Repository(); await repository.load(repositoryPath); @@ -192,19 +206,30 @@ describe('Adding objects from directories', function () { // Add some identical additional files // Add some new additional files - fs.writeFileSync(path.join(sourcePath1_additional_files, "sample", "file1.txt"), "$T)(*SKGJKVJS DFKJs"); - fs.writeFileSync(path.join(sourcePath1_additional_files, "sample", "file2.txt"), "$T)(*SKGJKdfsfVJS DFKJs"); + fs.writeFileSync( + path.join(sourcePath1_additional_files, "sample", "file1.txt"), + "$T)(*SKGJKVJS DFKJs" + ); + fs.writeFileSync( + path.join(sourcePath1_additional_files, "sample", "file2.txt"), + "$T)(*SKGJKdfsfVJS DFKJs" + ); const test_id = "id"; await repository.importNewObjectDir(test_id, sourcePath1); - const obj = await repository.importNewObjectDir(test_id, sourcePath1_additional_files); - + const obj = await repository.importNewObjectDir( + test_id, + sourcePath1_additional_files + ); const inv3 = await obj.getInventory(); const new_id = inv3.id; assert.strictEqual(new_id, test_id); // Check that the object is there - const objectPath = path.join(repositoryPath, new_id.replace(/(..)/g, "$1/")); + const objectPath = path.join( + repositoryPath, + new_id.replace(/(..)/g, "$1/") + ); assert.strictEqual(fs.existsSync(objectPath), true); // Check that it's v2 const object = new OcflObject(); @@ -212,39 +237,70 @@ describe('Adding objects from directories', function () { const inv = await object.getInventory(); assert.strictEqual(inv.versions["v2"].state[repeatedFileHash].length, 4); - assert.strictEqual(inv.versions["v2"].state[repeatedFileHash].indexOf("sample/lots_of_little_files/file_0-copy1.txt") > -1, true); - - // Now delete some stuff + assert.strictEqual( + inv.versions["v2"].state[repeatedFileHash].indexOf( + "sample/lots_of_little_files/file_0-copy1.txt" + ) > -1, + true + ); + + // Now delete some stuff await fs.remove(path.join(sourcePath1_additional_files, "sample", "pics")); // And re-import await repository.importNewObjectDir(test_id, sourcePath1_additional_files); // Re-initialize exsiting object const inv1 = await object.getInventory(); - // - assert.strictEqual(Object.keys(inv1.manifest).length, 211); + // + assert.strictEqual(Object.keys(inv1.manifest).length, 207); assert.strictEqual(inv1.manifest[sepiaPicHash][0], sepiaPicPath); // Sepia pic is v2 - assert.strictEqual(inv1.versions["v2"].state[sepiaPicHash][0], sepiaPicLogicalPath); + assert.strictEqual( + inv1.versions["v2"].state[sepiaPicHash][0], + sepiaPicLogicalPath + ); // Not in v3 assert.strictEqual(inv1.versions["v3"].state[sepiaPicHash], undefined); // Now put some stuff back - fs.copySync(path.join(sourcePath1, "sample", "pics"), path.join(sourcePath1_additional_files, "sample", "pics")); + fs.copySync( + path.join(sourcePath1, "sample", "pics"), + path.join(sourcePath1_additional_files, "sample", "pics") + ); await repository.importNewObjectDir(test_id, sourcePath1_additional_files); const inv2 = await object.getInventory(); - assert.strictEqual(Object.keys(inv1.manifest).length, 211); + assert.strictEqual(Object.keys(inv1.manifest).length, 207); assert.strictEqual(inv2.manifest[sepiaPicHash][0], sepiaPicPath); // Sepia pic is v2 - assert.strictEqual(inv2.versions["v4"].state[sepiaPicHash][0], sepiaPicLogicalPath, "no sepia pic in v4"); + assert.strictEqual( + inv2.versions["v4"].state[sepiaPicHash][0], + sepiaPicLogicalPath, + "no sepia pic in v4" + ); // Not in v3 - assert.strictEqual(inv2.versions["v3"].state[sepiaPicHash], undefined, "No sepia pic in v3"); + assert.strictEqual( + inv2.versions["v3"].state[sepiaPicHash], + undefined, + "No sepia pic in v3" + ); // No content dirs in V3 or v4 - assert.strictEqual(fs.existsSync(path.join(object.path, "v3", "content")), false), "v3 has no content dir"; - assert.strictEqual(fs.existsSync(path.join(object.path, "v4", "content")), false, "v4 has no content dir"); + assert.strictEqual( + fs.existsSync(path.join(object.path, "v3", "content")), + false + ), + "v3 has no content dir"; + assert.strictEqual( + fs.existsSync(path.join(object.path, "v4", "content")), + false, + "v4 has no content dir" + ); // Tho v2 has one - assert.strictEqual(fs.existsSync(path.join(object.path, "v2", "content")), true, "v2 has content dir"); + assert.strictEqual( + fs.existsSync(path.join(object.path, "v2", "content")), + true, + "v2 has content dir" + ); const exportDirV4 = path.join("test-data", "exportv4"); const exportDirV5 = path.join("test-data", "exportv5"); @@ -254,167 +310,203 @@ describe('Adding objects from directories', function () { await fs.remove(exportDirV4); await fs.remove(exportDirV5); - const testId = "id"; try { const init = await repository.export(testId, exportDirV4); } catch (e) { - assert.strictEqual(e.message, "Can't export as the directory does not exist.", "Export needs an empty directory to put stuff in."); + assert.strictEqual( + e.message, + "Can't export as the directory does not exist.", + "Export needs an empty directory to put stuff in." + ); } const fl = await fs.writeFile(exportDirV4, ""); try { const init = await repository.export(testId, exportDirV4); } catch (e) { - assert.strictEqual(e.message, "Can't export to an existing file.", "Cannot export over the top of a file"); + assert.strictEqual( + e.message, + "Can't export to an existing file.", + "Cannot export over the top of a file" + ); } await fs.remove(exportDirV4); await fs.mkdir(exportDirV4); await repository.export(testId, exportDirV4); - expect(exportDirV4).to.be.a.directory().and.deep.equal(sourcePath1_additional_files, "Matches the stuff that was imported", "Exported v4 is the same as the thing we imported."); - + expect(exportDirV4) + .to.be.a.directory() + .and.deep.equal( + sourcePath1_additional_files, + "Matches the stuff that was imported", + "Exported v4 is the same as the thing we imported." + ); try { const init = await repository.export(testId, exportDirV4); } catch (e) { - assert.strictEqual(e.message, "Can't export as the directory has stuff in it.", "Will not export to a directory that has existing content."); + assert.strictEqual( + e.message, + "Can't export as the directory has stuff in it.", + "Will not export to a directory that has existing content." + ); } await fs.mkdir(exportDirV1); await repository.export(testId, exportDirV1, { version: "v1" }); - expect(exportDirV1).to.be.a.directory().and.deep.equal(sourcePath1, "Matches the stuff that was imported"); + expect(exportDirV1) + .to.be.a.directory() + .and.deep.equal(sourcePath1, "Matches the stuff that was imported"); await fs.mkdir(exportDirV5); try { await repository.export(testId, exportDirV5, { version: "v5" }); } catch (e) { - assert.strictEqual(e.message, "Can't export a version that doesn't exist.", "Refuses to export non existent version"); + assert.strictEqual( + e.message, + "Can't export a version that doesn't exist.", + "Refuses to export non existent version" + ); } - }); - }); - // FIXME: a lot of this is duplicated from the directory import tests // and could be streamlined - -describe('Adding objects with callbacks', async function () { - +describe("Adding objects with callbacks", async function() { const CONTENT = { - 'dir/file1.txt': 'Contents of file1.txt', - 'dir/file2.txt': 'Contents of file2.txt', - 'file3.txt': 'Contents of file3.txt' + "dir/file1.txt": "Contents of file1.txt", + "dir/file2.txt": "Contents of file2.txt", + "file3.txt": "Contents of file3.txt" }; - const makeContent = async (dir) => { + let repository; + + beforeEach(async () => { + repository = await createTestRepo(); + }); + + const makeContent = async dir => { const files = Object.keys(CONTENT); - for( const f of files ) { + for (const f of files) { const d = path.join(dir, path.dirname(f)); await fs.ensureDir(d); await fs.writeFile(path.join(dir, f), CONTENT[f]); } }; - - it('can create an object with a callback', async function () { - const repository = await createTestRepo(); - const object = await repository.createNewObjectContent("some_id", makeContent); - assert.strictEqual(object.ocflVersion, '1.0'); + it("can create an object with a callback", async function() { + const object = await repository.createNewObjectContent( + "some_id", + makeContent + ); + assert.strictEqual(object.ocflVersion, "1.0"); }); - - it('Does not increment version number if you add the same thing twice', async function () { - const repository = await createTestRepo(); + it("Does not increment version number if you add the same thing twice", async function() { await repository.createNewObjectContent("xx", makeContent); const object = await repository.createNewObjectContent("xx", makeContent); const inventory = await object.getInventory(); - assert.strictEqual(inventory.head, 'v1'); + assert.strictEqual(inventory.head, "v1"); }); - it('Does not let you use a subset of an existing id', async function () { - const repository = await createTestRepo(); - await repository.createNewObjectContent("aaaa", makeContent); + it("Does not let you use a subset of an existing id", async function() { + await repository.createNewObjectContent("aa", makeContent); try { - const object = await repository.createNewObjectContent("aa", makeContent); + const object = await repository.createNewObjectContent( + "aabb", + makeContent + ); } catch (e) { - assert.strictEqual(e.message, 'There is no object here but the path already exists is this ID a subset of a longer one?'); - } + assert.strictEqual( + e.message, + "A parent of this path seems to be an OCFL object and that's not allowed" + ); + } }); - it('Does not let you use a superset of an existing id', async function () { - const repository = await createTestRepo(); + it("Does not let you use a superset of an existing id", async function() { await repository.createNewObjectContent("cc", makeContent); try { - await repository.createNewObjectContent("ccdd", makeContent); + await repository.createNewObjectContent("ccdd", makeContent); } catch (e) { - assert.strictEqual(e.message, 'Cannot make an object. There is already an object in a higher level directory.'); - } + assert.strictEqual( + e.message, + "A parent of this path seems to be an OCFL object and that's not allowed" + ); + } }); - - it('should make up an ID if you add content', async function () { - const repository = await createTestRepo(); + it("should make up an ID if you add content", async function() { const obj = await repository.createNewObjectContent(null, makeContent); const inv = await obj.getInventory(); const new_id = inv.id; // We got a UUID as an an ID assert.strictEqual(new_id.length, 36); // Check that the object is there - const objectPath = path.join(repositoryPath, new_id.replace(/(..)/g, "$1/")); + const objectPath = path.join( + repositoryPath, + new_id.replace(/(..)/g, "$1/") + ); assert.strictEqual(fs.existsSync(objectPath), true); }); - it('should use your id for a new object if you give it one', async function () { - const repository = await createTestRepo(); - const obj = await repository.createNewObjectContent("some_other_id", makeContent); + it("should use your id for a new object if you give it one", async function() { + const obj = await repository.createNewObjectContent( + "some_other_id", + makeContent + ); // We got a UUID as an an ID - const inv = await (obj.getInventory()); + const inv = await obj.getInventory(); assert.strictEqual(inv.id, "some_other_id"); // Check that the object is there - const objectPath = path.join(repositoryPath, inv.id.replace(/(..)/g, "$1/")); + const objectPath = path.join( + repositoryPath, + inv.id.replace(/(..)/g, "$1/") + ); assert.strictEqual(fs.existsSync(objectPath), true); }); - - - - it('should have the content generated by the callback', async function () { - const repository = await createTestRepo(); - const obj = await repository.createNewObjectContent("some_other_id", makeContent); + it("should have the content generated by the callback", async function() { + const obj = await repository.createNewObjectContent( + "some_other_id", + makeContent + ); const files = Object.keys(CONTENT); - for( const f of files ) { - const ocflf = path.join(obj.path, 'v1/content', f); - expect(ocflf).to.be.a.file(`${ocflf} is a file`).with.content(CONTENT[f]); + for (const f of files) { + const ocflf = path.join(obj.path, "v1/content", f); + expect(ocflf) + .to.be.a.file(`${ocflf} is a file`) + .with.content(CONTENT[f]); } - }) - - it('should have a manifest entry for each file with the correct hash', async function () { - const repository = await createTestRepo(); - const obj = await repository.createNewObjectContent("some_other_id", makeContent); + }); + + it("should have a manifest entry for each file with the correct hash", async function() { + const obj = await repository.createNewObjectContent( + "some_other_id", + makeContent + ); const files = Object.keys(CONTENT); const inventory = await obj.getInventory(); const manifest = inventory.manifest; - for( const f of files ) { - const ocflf = path.join(obj.path, 'v1/content', f); - expect(ocflf).to.be.a.file(`${ocflf} is a file`).with.content(CONTENT[f]); + for (const f of files) { + const ocflf = path.join(obj.path, "v1/content", f); + expect(ocflf) + .to.be.a.file(`${ocflf} is a file`) + .with.content(CONTENT[f]); const h = await hasha.fromFile(ocflf, { algorithm: DIGEST_ALGORITHM }); - expect(manifest[h][0]).to.equal(path.join('v1/content', f)); + expect(manifest[h][0]).to.equal(path.join("v1/content", f)); delete manifest[h]; } expect(manifest).to.be.empty; - }) - + }); }); - - -after(function () { +after(function() { //TODO: destroy test repoPath - });