Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5cf6f2d
feat: add user integration tests
devin-ai-integration[bot] Feb 7, 2025
0b95da2
feat: add real API integration tests for users
devin-ai-integration[bot] Feb 7, 2025
ce44b49
fix: use JWT auth instead of test token for integration tests
devin-ai-integration[bot] Feb 7, 2025
5d495e8
feat: add real API integration tests for users
devin-ai-integration[bot] Feb 7, 2025
4942d33
ci: add GitHub Actions workflow for integration tests
devin-ai-integration[bot] Feb 7, 2025
1cf32a8
ci: update test script to only run unit tests
devin-ai-integration[bot] Feb 7, 2025
bf3d0e9
ci: disable coverage for integration tests and allow failures until s…
devin-ai-integration[bot] Feb 7, 2025
e5bbcd2
fix: resolve linting issues in integration tests
devin-ai-integration[bot] Feb 7, 2025
7fb6364
ci: update integration test timeout to 60 seconds
devin-ai-integration[bot] Feb 7, 2025
7848535
fix: update mocha timeout configuration
devin-ai-integration[bot] Feb 7, 2025
f2deed5
fix: handle async operations properly in integration tests
devin-ai-integration[bot] Feb 7, 2025
4acf241
fix: handle async operations properly in integration tests
devin-ai-integration[bot] Feb 7, 2025
f3421bc
fix: add debug logging to integration test setup
devin-ai-integration[bot] Feb 7, 2025
144e572
fix: add more debug logging to environment setup
devin-ai-integration[bot] Feb 8, 2025
dc94c41
fix: improve environment setup reliability
devin-ai-integration[bot] Feb 8, 2025
dcffa08
fix: fix syntax errors in test helper
devin-ai-integration[bot] Feb 8, 2025
2b02df8
fix: handle interactive environment selection in test helper
devin-ai-integration[bot] Feb 8, 2025
13d4b3c
fix: improve environment setup reliability and add timeouts
devin-ai-integration[bot] Feb 8, 2025
18be64f
fix: reduce timeouts and add timing logs for better debugging
devin-ai-integration[bot] Feb 8, 2025
05b1dc9
fix: remove test:integration failure bypass
devin-ai-integration[bot] Feb 8, 2025
eb92bec
fix: resolve promise/avoid-new and promise/prefer-await-to-then lint …
devin-ai-integration[bot] Feb 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: integration-tests
on:
pull_request:
types: [opened, synchronize]
branches:
- main
push:
branches:
- main

jobs:
integration-test:
runs-on: ubuntu-latest
name: Integration Tests
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: '20'
- name: npm install
run: npm install
- name: Run Integration Tests
env:
BOX_JWT_CONFIG: ${{ secrets.BOX_JWT_CONFIG }}
BOX_ADMIN_USER_ID: ${{ secrets.BOX_ADMIN_USER_ID }}
run: npm run test:integration
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@
}
},
"scripts": {
"test": "nyc mocha \"test/**/*.test.js\"",
"test": "nyc mocha \"test/commands/**/*.test.js\"",
"test:integration": "mocha \"test/integration/__tests__/**/*.test.js\" --timeout 180000",
"posttest": "npm run lint",
"lint": "eslint --fix ./src ./test",
"clean": "find src/ -type d -name 'dist' -exec rm -rf {} +",
Expand Down
29 changes: 29 additions & 0 deletions test/integration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Integration Tests

## Setup
1. Create a Box Platform application with JWT authentication:
- Go to https://cloud.app.box.com/developers/console
- Create a new Custom App with "Server Authentication (with JWT)"
- Under "App Access Level" select "App + Enterprise Access"
- Enable all necessary application scopes
- Enable "Generate User Access Tokens" and "Make API calls using the as-user header"

2. Download the JWT configuration file:
- Go to your app's configuration tab
- In "App Settings" section, download the JSON configuration file
- Base64 encode the configuration file contents

3. Set environment variables:
- `BOX_JWT_CONFIG`: Base64 encoded JWT configuration
- `BOX_ADMIN_USER_ID`: ID of admin user account with sufficient permissions

## Running Tests
```bash
npm run test:integration
```

## Notes
- Integration tests require admin user access
- JWT configuration must have necessary permissions
- Tests will create and delete test data - use a development environment
- Never commit sensitive credentials or configuration
46 changes: 46 additions & 0 deletions test/integration/__tests__/users.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';

const { expect } = require('chai');
const { execCLI, getAdminUserId, setupEnvironment, cleanupEnvironment } = require('../helpers/test-helper');

describe('Users Integration Tests', () => {
let adminUserId;
let testUser;

beforeEach(async() => {
adminUserId = getAdminUserId();
await setupEnvironment();
});

after(async() => {
try {
if (testUser) {
await execCLI(`users:delete ${testUser.id} --force`);
}
} finally {
await cleanupEnvironment();
}
});

describe('User Operations', () => {
it('should get user info', async() => {
const output = await execCLI(`users:get ${adminUserId} --json`);
const user = JSON.parse(output);
expect(user.id).to.equal(adminUserId);
expect(user.type).to.equal('user');
expect(user.login).to.be.a('string');
expect(user.name).to.be.a('string');
});

it('should list group memberships', async() => {
const output = await execCLI(`users:groups ${adminUserId} --json`);
const memberships = JSON.parse(output);
expect(memberships).to.be.an('array');
memberships.forEach(membership => {
expect(membership.type).to.equal('group_membership');
expect(membership.user.id).to.equal(adminUserId);
expect(membership.group).to.be.an('object');
});
});
});
});
188 changes: 188 additions & 0 deletions test/integration/helpers/test-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
'use strict';

const path = require('path');
const fs = require('fs').promises;
const { promisify } = require('util');
const exec = promisify(require('child_process').exec);

const CLI_PATH = path.resolve(__dirname, '../../../bin/run');
const TIMEOUT = 30000; // 30 second timeout for operations

const getJWTConfig = () => {
const config = process.env.BOX_JWT_CONFIG;
if (!config) {
throw new Error('Missing BOX_JWT_CONFIG environment variable');
}
return JSON.parse(Buffer.from(config, 'base64').toString());
};

const getAdminUserId = () => {
const userId = process.env.BOX_ADMIN_USER_ID;
if (!userId) {
throw new Error('Missing BOX_ADMIN_USER_ID environment variable');
}
return userId;
};

async function execWithTimeout(command, timeoutMs = TIMEOUT) {
const timeoutError = new Error(`Command timed out after ${timeoutMs}ms: ${command}`);
const timeoutPromise = new Promise(resolve => {

Check failure on line 29 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 18 on ubuntu-latest

Avoid creating new promises

Check failure on line 29 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 20 on ubuntu-latest

Avoid creating new promises

Check failure on line 29 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 22 on ubuntu-latest

Avoid creating new promises

Check failure on line 29 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 18 on windows-latest

Avoid creating new promises

Check failure on line 29 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 20 on windows-latest

Avoid creating new promises

Check failure on line 29 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 22 on windows-latest

Avoid creating new promises

Check failure on line 29 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 18 on macos-latest

Avoid creating new promises

Check failure on line 29 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 20 on macos-latest

Avoid creating new promises

Check failure on line 29 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 22 on macos-latest

Avoid creating new promises
setTimeout(() => resolve(timeoutError), timeoutMs);
});

try {

Check failure on line 33 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 18 on ubuntu-latest

Unnecessary try/catch wrapper

Check failure on line 33 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 20 on ubuntu-latest

Unnecessary try/catch wrapper

Check failure on line 33 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 22 on ubuntu-latest

Unnecessary try/catch wrapper

Check failure on line 33 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 18 on windows-latest

Unnecessary try/catch wrapper

Check failure on line 33 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 20 on windows-latest

Unnecessary try/catch wrapper

Check failure on line 33 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 22 on windows-latest

Unnecessary try/catch wrapper

Check failure on line 33 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 18 on macos-latest

Unnecessary try/catch wrapper

Check failure on line 33 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 20 on macos-latest

Unnecessary try/catch wrapper

Check failure on line 33 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 22 on macos-latest

Unnecessary try/catch wrapper
const result = await Promise.race([exec(command), timeoutPromise]);
if (result === timeoutError) {
throw timeoutError;
}
return result;
} catch (error) {
throw error;
}
}

const setupEnvironment = async() => {
const startTime = Date.now();
const logWithTime = (msg) => {
const elapsed = Date.now() - startTime;
console.log(`[${elapsed}ms] ${msg}`);
};

logWithTime('Setting up test environment...');
const jwtConfig = getJWTConfig();
logWithTime('JWT config loaded');
const configPath = '/tmp/jwt-config.json';
const boxConfigDir = `${process.env.HOME}/.box`;

try {
// Create Box config directory if it doesn't exist
logWithTime('Creating Box config directory...');
try {
await fs.access(boxConfigDir);
} catch {
await fs.mkdir(boxConfigDir, { mode: 0o700 });
}

// Write JWT config
logWithTime('Writing JWT config file...');
await fs.writeFile(configPath, JSON.stringify(jwtConfig), { mode: 0o600 });

// Clean up existing environment with shorter timeout
logWithTime('Cleaning up existing environment...');
try {
await execWithTimeout(`${CLI_PATH} configure:environments:delete integration-test`, 10000);
logWithTime('Existing environment deleted');
} catch (error) {
logWithTime('No existing environment to delete');
}

// Add new environment with shorter timeout
logWithTime('Adding new environment...');
const { stdout: addOutput } = await execWithTimeout(
`${CLI_PATH} configure:environments:add ${configPath} --name=integration-test`,
15000
);
logWithTime('Add environment output: ' + addOutput);

// Set current environment with shorter timeout
logWithTime('Setting current environment...');
const setProcess = require('child_process').spawn(
CLI_PATH,
['configure:environments:set-current', 'integration-test'],
{ stdio: ['pipe', 'pipe', 'pipe'] }
);

let resolved = false;
const timeoutId = setTimeout(() => {
if (!resolved) {
setProcess.kill();
throw new Error('Environment selection timed out after 15s');
}
}, 15000);

// Handle environment selection with immediate response
setProcess.stdout.on('data', (data) => {
const output = data.toString();
logWithTime('Environment selection output: ' + output);
if (output.includes('Which environment?')) {
logWithTime('Selecting environment...');
setProcess.stdin.write('\n');
}
});

// Wait for process to complete with proper cleanup
const waitForProcess = async () => {
const cleanup = () => {
resolved = true;
clearTimeout(timeoutId);
setProcess.removeAllListeners();
};

try {
await new Promise((resolve, reject) => {

Check failure on line 122 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 18 on ubuntu-latest

Avoid creating new promises

Check failure on line 122 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 20 on ubuntu-latest

Avoid creating new promises

Check failure on line 122 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 22 on ubuntu-latest

Avoid creating new promises

Check failure on line 122 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 18 on windows-latest

Avoid creating new promises

Check failure on line 122 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 20 on windows-latest

Avoid creating new promises

Check failure on line 122 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 22 on windows-latest

Avoid creating new promises

Check failure on line 122 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 18 on macos-latest

Avoid creating new promises

Check failure on line 122 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 20 on macos-latest

Avoid creating new promises

Check failure on line 122 in test/integration/helpers/test-helper.js

View workflow job for this annotation

GitHub Actions / Node 22 on macos-latest

Avoid creating new promises
setProcess.on('close', (code) => {
cleanup();
if (code === 0) {
resolve();
} else {
reject(new Error(`Failed to set environment (exit code: ${code})`));
}
});

setProcess.on('error', (error) => {
cleanup();
reject(error);
});
});
} catch (error) {
cleanup();
throw error;
}
};

await waitForProcess();
logWithTime('Environment set successfully');

// Verify environment setup with shorter timeout
logWithTime('Verifying environment setup...');
const { stdout } = await execWithTimeout(`${CLI_PATH} users:get ${getAdminUserId()} --json`, 15000);
const user = JSON.parse(stdout);
if (!user.id || user.id !== getAdminUserId()) {
throw new Error('Failed to verify environment setup');
}
logWithTime('Environment verification complete');

} catch (error) {
console.error('Error setting up environment:', error);
throw error;
} finally {
// Clean up config file
try {
await fs.unlink(configPath);
} catch (error) {
console.error('Error cleaning up config file:', error);
}
}
};

const cleanupEnvironment = async() => {
try {
await execWithTimeout(`${CLI_PATH} configure:environments:delete integration-test`);
console.log('Environment cleanup complete');
} catch (error) {
console.error('Error cleaning up environment:', error);
}
};

const execCLI = async(command) => {
const { stdout } = await execWithTimeout(`${CLI_PATH} ${command}`);
return stdout;
};

module.exports = {
getJWTConfig,
getAdminUserId,
execCLI,
setupEnvironment,
cleanupEnvironment
};
Loading