@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
240 lines (191 loc) • 8.45 kB
text/typescript
import { VariableResolver } from '../variable-resolver.js';
describe('VariableResolver', () => {
let resolver: VariableResolver;
beforeEach(() => {
resolver = new VariableResolver();
});
describe('resolveVariables', () => {
it('should resolve simple string variables', async () => {
const input = 'Hello ${name}, welcome to ${place}!';
const context = { name: 'John', place: 'Atlas' };
const result = await resolver.resolveVariables(input, context);
expect(result).toBe('Hello John, welcome to Atlas!');
});
it('should handle missing variables', async () => {
const input = 'Hello ${name}, your score is ${score}';
const context = { name: 'Alice' }; // score is missing
const result = await resolver.resolveVariables(input, context);
expect(result).toBe('Hello Alice, your score is ${score}');
});
it('should resolve nested object paths', async () => {
const input = 'User ${user.name} from ${user.address.city}';
const context = {
user: {
name: 'Bob',
address: {
city: 'New York',
country: 'USA'
}
}
};
const result = await resolver.resolveVariables(input, context);
expect(result).toBe('User Bob from New York');
});
it('should resolve variables in objects', async () => {
const input = {
greeting: 'Hello ${name}',
message: 'Your balance is ${balance}',
nested: {
info: 'Location: ${location}'
}
};
const context = { name: 'Charlie', balance: 100, location: 'SF' };
const result = await resolver.resolveVariables(input, context);
expect(result).toEqual({
greeting: 'Hello Charlie',
message: 'Your balance is 100',
nested: {
info: 'Location: SF'
}
});
});
it('should resolve variables in arrays', async () => {
const input = ['Hello ${name}', 'Score: ${score}', { msg: '${message}' }];
const context = { name: 'Dave', score: 42, message: 'Well done!' };
const result = await resolver.resolveVariables(input, context);
expect(result).toEqual(['Hello Dave', 'Score: 42', { msg: 'Well done!' }]);
});
it('should handle non-string primitives', async () => {
const input = { num: 42, bool: true, nil: null };
const context = {};
const result = await resolver.resolveVariables(input, context);
expect(result).toEqual({ num: 42, bool: true, nil: null });
});
it('should handle complex nested structures', async () => {
const input = {
users: [
{ name: '${users.0.name}', role: '${users.0.role}' },
{ name: '${users.1.name}', role: '${users.1.role}' }
],
config: {
api: {
url: '${env.apiUrl}',
key: '${env.apiKey}'
}
}
};
const context = {
users: [
{ name: 'Admin', role: 'administrator' },
{ name: 'User', role: 'member' }
],
env: {
apiUrl: 'https://api.example.com',
apiKey: 'secret-key'
}
};
const result = await resolver.resolveVariables(input, context);
expect(result).toEqual({
users: [
{ name: 'Admin', role: 'administrator' },
{ name: 'User', role: 'member' }
],
config: {
api: {
url: 'https://api.example.com',
key: 'secret-key'
}
}
});
});
});
describe('evaluateCondition', () => {
it('should evaluate equality conditions', async () => {
const context = { status: 'active', count: 5 };
expect(await resolver.evaluateCondition('status == active', context)).toBe(true);
expect(await resolver.evaluateCondition('status === active', context)).toBe(true);
expect(await resolver.evaluateCondition('status == inactive', context)).toBe(false);
expect(await resolver.evaluateCondition('count == 5', context)).toBe(true);
});
it('should evaluate inequality conditions', async () => {
const context = { status: 'active', count: 5 };
expect(await resolver.evaluateCondition('status != inactive', context)).toBe(true);
expect(await resolver.evaluateCondition('status !== inactive', context)).toBe(true);
expect(await resolver.evaluateCondition('count != 10', context)).toBe(true);
});
it('should evaluate comparison conditions', async () => {
const context = { count: 5, price: 99.99 };
expect(await resolver.evaluateCondition('count > 3', context)).toBe(true);
expect(await resolver.evaluateCondition('count >= 5', context)).toBe(true);
expect(await resolver.evaluateCondition('count < 10', context)).toBe(true);
expect(await resolver.evaluateCondition('count <= 5', context)).toBe(true);
expect(await resolver.evaluateCondition('price > 100', context)).toBe(false);
});
it('should evaluate boolean literals', async () => {
const context = {};
expect(await resolver.evaluateCondition('true', context)).toBe(true);
expect(await resolver.evaluateCondition('false', context)).toBe(false);
});
it('should evaluate variable existence', async () => {
const context = { hasFeature: true, emptyValue: '', zero: 0 };
expect(await resolver.evaluateCondition('hasFeature', context)).toBe(true);
expect(await resolver.evaluateCondition('missingVar', context)).toBe(false);
expect(await resolver.evaluateCondition('emptyValue', context)).toBe(false);
expect(await resolver.evaluateCondition('zero', context)).toBe(false); // 0 is falsy
});
it('should resolve variables in conditions', async () => {
const context = {
user: { role: 'admin' },
threshold: 10,
value: 15
};
expect(await resolver.evaluateCondition('${user.role} == admin', context)).toBe(true);
expect(await resolver.evaluateCondition('${value} > ${threshold}', context)).toBe(true);
});
it('should handle string comparisons with quotes', async () => {
const context = { message: 'hello world' };
expect(await resolver.evaluateCondition('message == "hello world"', context)).toBe(true);
expect(await resolver.evaluateCondition("message == 'hello world'", context)).toBe(true);
expect(await resolver.evaluateCondition('message == "goodbye"', context)).toBe(false);
});
it('should handle numeric comparisons', async () => {
const context = { strNum: '42', actualNum: 42 };
expect(await resolver.evaluateCondition('strNum > 40', context)).toBe(true);
expect(await resolver.evaluateCondition('actualNum > 40', context)).toBe(true);
});
it('should handle complex expressions with variables', async () => {
const context = {
order: { total: 150, discount: 20 },
user: { isPremium: true }
};
const condition = '${order.total} > 100';
expect(await resolver.evaluateCondition(condition, context)).toBe(true);
});
it('should return false for malformed conditions', async () => {
const context = { value: 10 };
expect(await resolver.evaluateCondition('invalid expression @#$', context)).toBe(false);
expect(await resolver.evaluateCondition('', context)).toBe(false);
});
});
describe('evaluateExpression', () => {
it('should evaluate simple expressions', async () => {
const context = { price: 100, quantity: 3 };
// For now, just variable substitution
const result = await resolver.evaluateExpression('Total: ${price} x ${quantity}', context);
expect(result).toBe('Total: 100 x 3');
});
it('should handle nested variable paths in expressions', async () => {
const context = {
product: {
name: 'Widget',
price: { amount: 99.99, currency: 'USD' }
}
};
const result = await resolver.evaluateExpression(
'${product.name} costs ${product.price.amount} ${product.price.currency}',
context
);
expect(result).toBe('Widget costs 99.99 USD');
});
});
});