Milestone 0: import zappier billing, Verae middleware, and Zapier research

Compose-ready workspace: packages/zappier (rate card, portal, Stripe),
packages/verae-zapier-middleware (timestamp + NATS), packages/verae-zapier
(CLI app), vendor/zapier-platform, and research/zapier vendor corpus.

Gate 0 structure checks pass. Product code and research are not yet wired.
This commit is contained in:
George Lambert 2026-09-09 02:37:36 -04:00
commit b4150c8250
1364 changed files with 6814366 additions and 0 deletions

View file

@ -0,0 +1,75 @@
require('should');
const zapier = require('zapier-platform-core');
const testUtils = require('./test-utils');
const App = require('../index');
const appTester = zapier.createAppTester(App);
describe('Authentication', () => {
before(testUtils.globalBeforeSetup);
it('should refresh auth', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
};
appTester(App.authentication.oauth2Config.refreshAccessToken, bundle)
.then((result) => {
result.should.have.property('access_token');
result.should.have.property('refresh_token');
done();
})
.catch(done);
});
it('should test auth', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
accountType: 'personal',
},
};
appTester(App.authentication.test, bundle)
.then((result) => {
result.should.have.property('userPrincipalName');
done();
})
.catch(done);
});
it('should generate an authorize URL', (done) => {
const bundle = {
// When an app runs on zapier.com, these will be generated by Zapier and
// passed to your app in the bundle. For the purpose of this test, we
// create a bundle with dummy values to ensure our function works.
inputData: {
state: '4444',
redirect_uri: 'https://zapier.com/',
accountType: 'personal',
},
environment: {
CLIENT_ID: '1234',
CLIENT_SECRET: 'asdf',
},
};
appTester(App.authentication.oauth2Config.authorizeUrl, bundle)
.then((authorizeUrl) => {
authorizeUrl.should.eql(
'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=1234&redirect_uri=http%3A%2F%2Fzapier.com%2F&response_type=code&state=4444',
);
done();
})
.catch(done);
});
});

View file

@ -0,0 +1,112 @@
'use strict';
require('should');
const zapier = require('zapier-platform-core');
const testUtils = require('../test-utils');
const App = require('../../index');
const appTester = zapier.createAppTester(App);
const TEST_RESOURCES = testUtils.TEST_RESOURCES;
describe('Create Text File', () => {
before(testUtils.globalBeforeSetup);
it('should create a new text file in the root dir', (done) => {
const file = 'Sample content';
const name = `Test-${new Date().getTime()}`;
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: '',
file,
name,
},
};
appTester(App.creates.textFile.operation.perform, bundle)
.then((result) => {
result.should.have.property('id');
result.should.have.property('file');
result.should.not.have.property('folder');
result.name.should.containEql(name);
result.name.should.containEql('.txt');
result.name.should.not.containEql('.txt.txt');
result._parent.should.eql('');
result._path.should.containEql(`/${name}`);
done();
})
.catch(done);
});
it('should create a text file with a unicode name and contents', (done) => {
const file = 'Ohweeeeee®™¥¶‰÷!!!!!!!!!!!';
const name = `Test-ç€is-æ-${new Date().getTime()}.txt`;
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: TEST_RESOURCES.folder.path,
name,
file,
},
};
appTester(App.creates.textFile.operation.perform, bundle)
.then((result) => {
result.should.have.property('id');
result.should.have.property('file');
result.should.not.have.property('folder');
result.name.should.containEql(name);
result.name.should.containEql('.txt');
result.name.should.not.containEql('.txt.txt');
result._parent.should.eql(TEST_RESOURCES.folder.parent);
result._path.should.containEql(`${TEST_RESOURCES.folder.path}/${name}`);
done();
})
.catch(done);
});
it('should create a new text file in the root dir, renaming it, because another one exists', (done) => {
const file = 'Sample content';
const name = 'boom';
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: '',
file,
name,
},
};
appTester(App.creates.textFile.operation.perform, bundle)
.then((result) => {
result.should.have.property('id');
result.should.have.property('file');
result.should.not.have.property('folder');
result.name.should.containEql(`${name} `);
result.name.should.containEql('.txt');
result.name.should.not.containEql('.txt.txt');
result.name.should.not.containEql(`${name}.txt`);
result._parent.should.eql('');
result._path.should.containEql(`/${name} `);
done();
})
.catch(done);
});
});

View file

@ -0,0 +1,47 @@
require('should');
const zapier = require('zapier-platform-core');
const testUtils = require('./test-utils');
const App = require('../index');
const appTester = zapier.createAppTester(App);
const TEST_RESOURCES = testUtils.TEST_RESOURCES;
describe('Hydrators', () => {
before(testUtils.globalBeforeSetup);
it('should get file contents', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
},
inputData: {
id: TEST_RESOURCES.root.id,
},
};
appTester(App.hydrators.getFileContents, bundle)
.then((fileContents) => {
fileContents.length.should.above(0);
done();
})
.catch(done);
});
it('should get unicode file contents', (done) => {
// CODE TIP: Hydrating files often involves touching the raw bytes (and
// handling the file names), so it's a good idea to test different edge cases
done();
});
it('should not get file contents for big files', (done) => {
// An example edge case that would be worth testing
done();
});
it('should not get file contents for binary files', (done) => {
// An example edge case that would be worth testing
done();
});
});

View file

@ -0,0 +1,186 @@
'use strict';
require('should');
const zapier = require('zapier-platform-core');
const testUtils = require('../test-utils');
const App = require('../../index');
const appTester = zapier.createAppTester(App);
const TEST_RESOURCES = testUtils.TEST_RESOURCES;
describe('File Resource', () => {
before(testUtils.globalBeforeSetup);
it('should get an existing file in the root folder', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
id: TEST_RESOURCES.root.id,
},
};
appTester(App.resources.file.get.operation.perform, bundle)
.then((result) => {
result.should.have.property('id');
result.should.have.property('file');
result.should.not.have.property('folder');
result.name.should.eql(TEST_RESOURCES.root.name);
result._path.should.eql(TEST_RESOURCES.root.path);
result._parent.should.eql(TEST_RESOURCES.root.parent);
done();
})
.catch(done);
});
it('should get an existing file in a child folder', (done) => {
// An example scenario that could be worth testing if the HTTP request for
// a child folder is sufficiently different from a root folder
done();
});
it('should list files in root dir', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: '',
},
};
appTester(App.resources.file.list.operation.perform, bundle)
.then((results) => {
results.length.should.above(0);
results.forEach((result) => {
result.should.have.property('id');
result.should.have.property('file');
result.should.not.have.property('folder');
});
done();
})
.catch(done);
});
it('should not miss any fields from the sample', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: '',
},
};
appTester(App.resources.file.list.operation.perform, bundle)
.then((results) => {
results.length.should.above(0);
results.forEach((result) => {
result.should.have.property('id');
result.should.have.property('file');
result.should.not.have.property('folder');
});
const file = results[0];
const sampleKeys = Object.keys(App.resources.file.sample);
sampleKeys.forEach((sampleKey) => file[sampleKey].should.eql(true));
done();
})
.catch(done);
});
it('should upload a new file in the root dir', (done) => {
const file =
'https://cdn.zapier.com/storage/files/f6679cf77afeaf6b8426de8d7b9642fc.pdf';
const name = `Test-${new Date().getTime()}-ç€is-æ.pdf`;
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: '',
file,
name,
},
};
appTester(App.resources.file.create.operation.perform, bundle)
.then((result) => {
result.should.have.property('id');
result.should.have.property('file');
result.should.not.have.property('folder');
result.name.should.containEql(name);
result._parent.should.eql('');
result._path.should.containEql(`/${name}`);
done();
})
.catch(done);
});
it('should upload an utf8-named file', (done) => {
// CODE TIP: It's a good idea to write multiple tests cases around creates
// to verify some of the common edges:
// * Non-ascii data
// * Records associated with the right relationships (i.e. a file in a folder, an owner of a lead)
// * Critical defaults work as expected
// * Dates & times are set correctly
done();
});
it('should upload a new file in the root dir, renaming it, because another one exists', (done) => {
// An example of an edge case where a naming collision in OneDrive could
// cause an overwrite or an auto-renaming of the file, so we want to test
// and make sure we get the behavior we want (auto-renaming)
done();
});
it('should find a file in the root dir', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: '',
name: 'sample',
},
};
appTester(App.resources.file.search.operation.perform, bundle)
.then((results) => {
results.length.should.above(0);
results[0].should.have.property('id');
results[0].should.have.property('file');
results[0].should.not.have.property('folder');
results[0].name.should.containEql('sample');
results[0]._parent.should.eql('');
results[0]._path.should.containEql('/sample');
done();
})
.catch(done);
});
it('should find a file in a child dir', (done) => {
// An example scenario that could be worth testing if the HTTP request for
// a child folder is sufficiently different from a root folder
done();
});
it('should find a unicode file in a child dir', (done) => {
// CODE TIP: Like with creates, adding multiple tests around the common
// edge cases of searching is a good idea
done();
});
});

View file

@ -0,0 +1,247 @@
'use strict';
require('should');
const zapier = require('zapier-platform-core');
const testUtils = require('../test-utils');
const App = require('../../index');
const appTester = zapier.createAppTester(App);
const TEST_RESOURCES = testUtils.TEST_RESOURCES;
describe('Folder Resource', () => {
before(testUtils.globalBeforeSetup);
it('should get an existing root folder', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
id: TEST_RESOURCES.folder.id,
},
};
appTester(App.resources.folder.get.operation.perform, bundle)
.then((result) => {
result.should.have.property('id');
result.should.have.property('folder');
result.should.not.have.property('file');
result.name.should.eql(TEST_RESOURCES.root.name);
result._path.should.eql(TEST_RESOURCES.root.path);
result._parent.should.eql(TEST_RESOURCES.root.parent);
done();
})
.catch(done);
});
it('should get an existing child folder', (done) => {
// An example scenario that could be worth testing if the HTTP request for
// a child folder is sufficiently different from a root folder
done();
});
it('should list folders in root dir', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: '',
},
meta: {
prefill: false,
},
};
appTester(App.resources.folder.list.operation.perform, bundle)
.then((results) => {
results.length.should.above(0);
results.forEach((result) => {
result.should.have.property('id');
result.should.have.property('folder');
result.should.not.have.property('file');
});
done();
})
.catch(done);
});
it('should list folders without parents on no prefill', (done) => {
// CODE TIP: When reusing a list operation to power a trigger and a
// dynmaic dropdown (prefill), it's a good idea to test both scenarios if the
// output from the operation varies between the two contexts
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: TEST_RESOURCES.childFolder.path,
},
meta: {
prefill: false,
},
};
appTester(App.resources.folder.list.operation.perform, bundle)
.then((results) => {
results.length.should.above(0);
let foundParents = false;
results.forEach((result) => {
result.should.have.property('id');
result.should.have.property('folder');
result.should.not.have.property('file');
if (result.id < 0) {
foundParents = true;
}
});
foundParents.should.eql(false);
done();
})
.catch(done);
});
it('should list folders with parents on prefill', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: TEST_RESOURCES.childFolder.path,
},
meta: {
prefill: true,
},
};
appTester(App.resources.folder.list.operation.perform, bundle)
.then((results) => {
results.length.should.above(0);
let foundParents = false;
results.forEach((result) => {
result.should.have.property('id');
result.should.have.property('folder');
result.should.not.have.property('file');
if (result.id < 0) {
foundParents = true;
}
});
foundParents.should.eql(true);
done();
})
.catch(done);
});
it('should not miss any fields from the sample', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: '',
},
meta: {
prefill: false,
},
};
appTester(App.resources.folder.list.operation.perform, bundle)
.then((results) => {
results.length.should.above(0);
results.forEach((result) => {
result.should.have.property('id');
result.should.have.property('folder');
result.should.not.have.property('file');
});
const folder = results[0];
const sampleKeys = Object.keys(App.resources.folder.sample);
sampleKeys.forEach((sampleKey) => folder[sampleKey].should.eql(true));
done();
})
.catch(done);
});
it('should create a new folder in the root dir', (done) => {
const name = `Test-${new Date().getTime()}`;
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: '',
name,
},
};
appTester(App.resources.folder.create.operation.perform, bundle)
.then((result) => {
result.should.have.property('id');
result.should.have.property('folder');
result.should.not.have.property('file');
result.name.should.containEql(name);
result._parent.should.eql('');
result._path.should.containEql(`/${name}`);
done();
})
.catch(done);
});
it('should create a new folder in a child dir', (done) => {
// An example scenario that could be worth testing if the HTTP request for
// a child folder is sufficiently different from a root folder
done();
});
it('should find a folder in the root dir', (done) => {
const bundle = {
authData: {
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
inputData: {
folder: '',
name: 'Documents',
},
};
appTester(App.resources.folder.search.operation.perform, bundle)
.then((results) => {
results.length.should.above(0);
results[0].should.have.property('id');
results[0].should.have.property('folder');
results[0].should.not.have.property('file');
results[0].name.should.containEql('Documents');
results[0]._parent.should.eql('');
results[0]._path.should.containEql('/Documents');
done();
})
.catch(done);
});
it('should find a folder in a child dir', (done) => {
// An example scenario that could be worth testing if the HTTP request for
// a child folder is sufficiently different from a root folder
done();
});
});

View file

@ -0,0 +1,83 @@
'use strict';
require('should');
const zapier = require('zapier-platform-core');
const App = require('../index');
const appTester = zapier.createAppTester(App);
const globalBeforeSetup = (done) => {
zapier.tools.env.inject();
if (
!process.env.CLIENT_ID ||
!process.env.CLIENT_SECRET ||
!process.env.REFRESH_TOKEN
) {
throw new Error(
'Setup your .environment file (or use `export`) according to the README.',
);
}
// ACCESS_TOKEN expires very quickly, so we get a new one per test run from REFRESH_TOKEN
if (!process.env.ACCESS_TOKEN) {
const bundle = {
authData: {
access_token: '',
refresh_token: process.env.REFRESH_TOKEN,
accountType: 'personal',
},
};
appTester(App.authentication.oauth2Config.refreshAccessToken, bundle)
.then((result) => {
result.should.have.property('access_token');
result.should.have.property('refresh_token');
process.env.ACCESS_TOKEN = result.access_token;
done();
})
.catch(done);
} else {
done();
}
};
// Below is a set of files the app can use to test different scenarios.
// To make the test suite run, add files to your account in the given folders,
// then fill in the IDs of files from your account. The `id` is visible in the
// URL when accessing the file in the browser, for example:
// URL: https://onedrive.live.com/?cid=697LD7B578EB1372&id=697LD7B578EB1372%21716
// ID: 697LD7B578EB1372!716
const TEST_RESOURCES = {
root: {
id: '',
name: 'sample.pdf',
path: '/sample.pdf',
parent: '',
},
nested: {
id: '',
name: 'test.txt',
path: '/Documents/Testing/test.txt',
parent: '/Documents/Testing',
},
folder: {
id: '',
name: 'Documents',
path: '/Documents',
parent: '',
},
childFolder: {
id: '',
name: 'Testing',
path: '/Documents/Testing',
parent: '/Documents',
},
};
module.exports = {
globalBeforeSetup,
TEST_RESOURCES,
};

View file

@ -0,0 +1,68 @@
'use strict';
require('should');
const utils = require('../utils');
const extractParentsFromPath = utils.extractParentsFromPath;
const getStringByteSize = utils.getStringByteSize;
describe('Utils', () => {
it('should extract parents from a folder path', () => {
let parents = extractParentsFromPath('/Some/Kind of /Test');
parents.length.should.eql(3);
parents[0].id.should.eql(-1);
parents[0].name.should.eql('/');
parents[0]._path.should.eql('');
parents[0].should.have.property('folder');
parents[0].should.not.have.property('file');
parents[1].id.should.eql(-2);
parents[1].name.should.eql('Some');
parents[1]._path.should.eql('/Some');
parents[1].should.have.property('folder');
parents[1].should.not.have.property('file');
parents[2].id.should.eql(-3);
parents[2].name.should.eql('Kind of ');
parents[2]._path.should.eql('/Some/Kind of ');
parents[2].should.have.property('folder');
parents[2].should.not.have.property('file');
parents = extractParentsFromPath('/Zapier');
parents.length.should.eql(1);
parents[0].id.should.eql(-1);
parents[0].name.should.eql('/');
parents[0]._path.should.eql('');
parents[0].should.have.property('folder');
parents[0].should.not.have.property('file');
});
it('should correctly compute the byte size of strings', () => {
const tests = [
{
string: 'asd',
expected: 3,
},
{
string: '@€£¶‰dfg',
expected: 14,
},
{
string: '÷¶[]÷d()ß-°ú',
expected: 18,
},
{
string: 'ç€is-æ',
expected: 10,
},
];
tests.forEach((test) =>
getStringByteSize(test.string).should.eql(test.expected),
);
});
});