Skip to content

Commit

Permalink
Added tests for context.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jim Buck committed Oct 11, 2018
1 parent ecaa7e2 commit d67f436
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 29 deletions.
16 changes: 8 additions & 8 deletions src/lib/builders/collection-builder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,12 @@ test(`CollectionBuilder#create accepts an override of constants`, t => {
});

function createMockActivator(cb?: (Type: Constructor, count?: number | Lookup, constants?: Lookup) => void) {
class MockActivator extends Activator {
public create<T>(Type: Constructor<T>, count?: number | Lookup, constants?: Lookup): GeneratedArray<T> {
cb && cb(Type, count, constants);
return [];
}
}

return new MockActivator();
const activator = new Activator();

activator['create'] = (Type: Constructor, count?: number | Lookup, constants?: Lookup) => {
cb && cb(Type, count, constants);
return [] as GeneratedArray;
};

return activator;
}
11 changes: 11 additions & 0 deletions src/lib/models/list-bucket.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,15 @@ test(`ListBucket#toJSON`, t => {
t.is(data.length, 6);
let actualJson = JSON.stringify(bucket);
t.is(actualJson, expectedJson);
});

test(`ListBucket#forEachKey`, t => {
const key = 'nums';
let bucket = new ListBucket();
let expectedRound1 = [1, 3, 5];
let expectedRound2 = [2, 4, 6];
bucket.add(key, expectedRound1);
bucket.add(key, expectedRound2);
let data = bucket.get(key);
t.is(data.length, 6);
});
4 changes: 4 additions & 0 deletions src/lib/models/list-bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export class ListBucket {
this._data = {};
}

public forEachKey(cb: (key: string) => void): void {
Object.keys(this._data).forEach(cb);
}

private toJSON() {
return this._data;
}
Expand Down
25 changes: 17 additions & 8 deletions src/lib/services/activator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Lookup, Constructor, GuidType, EnumType } from '../models/types';
import { Lookup, Constructor, GuidType, EnumType, GeneratedConstructor } from '../models/types';
import { StoredEnum } from '../models/stored-enum';
import { PropertyDefinition } from '../models/property-definition';
import { ModelDefinition } from '../models/model-definition';
Expand All @@ -10,10 +10,12 @@ import { Model } from '../..';
export class Activator {

private _rand: Random;
private _cache: ListBucket;
private _data: ListBucket;
private _types: Lookup<GeneratedConstructor>;

constructor(seed?: number) {
this._cache = new ListBucket();
this._data = new ListBucket();
this._types = {};
this._rand = new Random(seed);
}

Expand All @@ -38,20 +40,27 @@ export class Activator {
}

let results = Array(count).fill(0).map(() => this._createModel(Type as Constructor<T>, modelDef, constants));
return this._cache.add(modelDef.id, results);
this._types[modelDef.id] = Type;
return this._data.add(modelDef.id, results);
} else {
let crossed = countOrCrossed;
let results = crossed.map(cross => this._createModel(Type as Constructor<T>, modelDef, Object.assign({}, cross, constants)));
return this._cache.add(modelDef.id, results);
this._types[modelDef.id] = Type;
return this._data.add(modelDef.id, results);
}
}

public data() {
return this._cache;
public get data() {
return this._data;
}

public get types() {
return this._types;
}

public clear() {
this._cache.clear();
this._data.clear();
this._types = {};
}

private _createModel<T>(Type: Constructor<T>, modelDef: ModelDefinition, constants: Lookup<any> = {}): T {
Expand Down
256 changes: 246 additions & 10 deletions src/lib/services/context.spec.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,266 @@
import { test } from 'ava';

import { Context } from './context';
import { Constructor, Lookup, GeneratedArray } from '../models/types';
import { Activator } from './activator';
import { StaticCreate } from '../builders/model-builder';

const EMPTY_JSON_OBJECT = '{}';

test(`Optionally accepts a seed`, t => {
let ctx1 = new Context();
const ctx1 = new Context();
t.true(ctx1 instanceof Context);
t.is(typeof ctx1.seed, 'number');

const EXPECTED_SEED = 123;
let ctx2 = new Context(EXPECTED_SEED);
const ctx2 = new Context(EXPECTED_SEED);
t.true(ctx2 instanceof Context);
t.is(ctx2.seed, EXPECTED_SEED);
});

test.todo(`Context#using returns a populated CollectionBuilder`);
test(`Context#using returns a populated CollectionBuilder`, t => {
const ctx = new Context();
const expectedCollection = [2, 4, 6];

const expectedKey = 'test';
const expectedRefs = { [expectedKey]: expectedCollection };
const collection = ctx.using(expectedRefs);

t.deepEqual(collection['_explicitRefs'].get(expectedKey), expectedRefs[expectedKey]);
});

test(`Context#cross returns a populated CollectionBuilder`, t => {
const ctx = new Context();

const expectedRefs = { 'evens': [2, 4, 6], 'odds': [1, 3, 5] };
const collection = ctx.cross(expectedRefs);

t.deepEqual(collection['_crossRefs'], expectedRefs);
});

// Maches CollectionBuilder#create
test(`Context#create requires count or cross`, t => {
const Num = StaticCreate('Num').key('id', id => id.choices([1, 2])).build();

const ctx = createContext();

t.throws(() => ctx.create(Num, null));
});

// Maches CollectionBuilder#create
test(`Context#create returns exact count for non-crossed`, t => {
const Num = StaticCreate('Num').key('id', id => id.guid()).build();

const expectedCount = 7;

const ctx = createContext((Type, count, constants) => {
t.is(count, expectedCount);
});

ctx.create(Num, expectedCount);
});

// Maches CollectionBuilder#create
test(`Context#create returns result for each cross`, t => {
const color = ['red', 'white', 'blue'];
const num = [1, 2];

const Col = StaticCreate('Col').key('id', c => c.choices(color)).build();
const Num = StaticCreate('Num').key('id', id => id.choices(num)).build();
const TestThing = StaticCreate('TestThing')
.key('id', id => id.guid())
.ref('color', Col)
.ref('num', Num)
.build();

const expectedRefs = [
{ color: 'red', num: 1 }, { color: 'red', num: 2 },
{ color: 'white', num: 1 }, { color: 'white', num: 2 },
{ color: 'blue', num: 1 }, { color: 'blue', num: 2 }];

const crosses = {
color: [new Col({ id: 'red' }), new Col({ id: 'white' }), new Col({ id: 'blue' })],
num: [new Num({ id: 1 }), new Num({ id: 2 })]
};

const ctx = createContext((Type, cross, constants) => {
t.deepEqual(cross, expectedRefs);
});

ctx.cross(crosses).create(TestThing);
});

// Maches CollectionBuilder#create
test(`Context#create requires each list to have one or more generated items`, t => {
const Col = StaticCreate('Col').key('id', c => c.choices(['red', 'white', 'blue'])).build();
const Num = StaticCreate('Num').key('id', id => id.choices([1, 2])).build();
const TestThing = StaticCreate('TestThing')
.key('id', id => id.guid())
.ref('color', Col)
.ref('num', Num)
.build();

const invalidColor = [{ id: 'red' }, { id: 'white' }, { id: 'blue' }];
const validColor = [new Col({ id: 'red' }), new Col({ id: 'white' }), new Col({ id: 'blue' })];

const invalidNum = [{ id: 1 }, { id: 2 }];
const validNum = [new Num({ id: 1 }), new Num({ id: 2 })];

const ctx = createContext();

t.throws(() => ctx.cross({ color: invalidColor, num: validNum }).create(TestThing));
t.throws(() => ctx.cross({ color: validColor, num: invalidNum }).create(TestThing));
t.throws(() => ctx.cross({ color: invalidColor, num: invalidNum }).create(TestThing));
t.notThrows(() => ctx.cross({ color: validColor, num: validNum }).create(TestThing));
});

// Maches CollectionBuilder#create
test(`Context#create accepts an override of constants`, t => {
const Num = StaticCreate('Num')
.key('id', id => id.guid())
.prop('name', n => n.str(30))
.build();

test.todo(`Context#cross returns a populated CollectionBuilder`);
const expectedConstants = {
name: 'constant-name'
};

test.todo(`Context#create returns a populated array`);
const ctx = createContext((Type, count, constants) => {
t.deepEqual(constants, expectedConstants);
});

test.todo(`Context#clear empties out the activator's cache`);
ctx.create(Num, 3, expectedConstants);
});

test(`Context#json returns a string of the generated data`, t => {
const Num = StaticCreate('Num')
.key('id', id => id.integer(1, 3))
.prop('name', n => n.str(30))
.build();

const ctx = createContext();

const expectedData = {
Num: [
new Num({ id: 1, name: 'red' }),
new Num({ id: 3, name: 'white' }),
new Num({ id: 2, name: 'blue' })]
};
const expectedJson = JSON.stringify(expectedData);

changeActivatorData(ctx, expectedData);

const json = ctx.json();
t.is(json, expectedJson);
});

test(`Context#json accepts a format flag`, t => {
const Num = StaticCreate('Num')
.key('id', id => id.integer(1, 3))
.prop('name', n => n.str(30))
.build();

const ctx = createContext();

const expectedData = {
Num: [
new Num({ id: 1, name: 'red' }),
new Num({ id: 3, name: 'white' }),
new Num({ id: 2, name: 'blue' })]
};
const expectedJson = JSON.stringify(expectedData, null, '\t');

changeActivatorData(ctx, expectedData);

const json = ctx.json(true);
t.is(json, expectedJson);
});

test(`Context#json accepts a format character`, t => {
const Num = StaticCreate('Num')
.key('id', id => id.integer(1, 3))
.prop('name', n => n.str(30))
.build();

const formatChar = ' ';
const ctx = createContext();

const expectedData = {
Num: [
new Num({ id: 1, name: 'red' }),
new Num({ id: 3, name: 'white' }),
new Num({ id: 2, name: 'blue' })]
};
const expectedJson = JSON.stringify(expectedData, null, formatChar);

changeActivatorData(ctx, expectedData);

const json = ctx.json(formatChar);
t.is(json, expectedJson);
});

test(`Context#data returns a raw clone of the generated data`, t => {
const Num = StaticCreate('Num')
.key('id', id => id.integer(1, 3))
.prop('name', n => n.str(30))
.build();

const ctx = createContext();

const expectedData = {
Num: [
new Num({ id: 1, name: 'red' }),
new Num({ id: 3, name: 'white' }),
new Num({ id: 2, name: 'blue' })
]
};

changeActivatorData(ctx, expectedData, { Num });

let data = ctx.data();
t.deepEqual(data, expectedData);
});

test(`Context#clear empties out the activator's cache`, t => {
const Num = StaticCreate('Num')
.key('id', id => id.integer(1, 3))
.prop('name', n => n.str(30))
.build();

const ctx = createContext();

const expectedData = {
Num: [
new Num({ id: 1, name: 'red' }),
new Num({ id: 3, name: 'white' }),
new Num({ id: 2, name: 'blue' })
]
};
const expectedJson = JSON.stringify(expectedData);

changeActivatorData(ctx, expectedData);

let json = ctx.json();
t.is(json, expectedJson);
ctx.clear();
json = ctx.json();
t.is(json, EMPTY_JSON_OBJECT);
});

test.todo(`Context#json returns a string of the generated data`);
function createContext(cb?: (Type: Constructor, count?: number | Lookup, constants?: Lookup) => void) {
const ctx = new Context();
const activator = new Activator();

test.todo(`Context#json accepts a format flag`);
activator['create'] = (Type: Constructor, count?: number | Lookup, constants?: Lookup) => {
cb && cb(Type, count, constants);
return [] as GeneratedArray;
};

test.todo(`Context#json accepts a format character`);
ctx['_activator'] = activator;
return ctx;
}

test.todo(`Context#data returns a clone of the generated data`);
function changeActivatorData(ctx: Context, data: Lookup, types: Lookup = {}) {
ctx['_activator']['_data']['_data'] = data;
ctx['_activator']['_types'] = types;
}
Loading

0 comments on commit d67f436

Please sign in to comment.