Add more detailed error information when writing the wrong type (#1202)

This commit is contained in:
Erik Arvidsson
2016-04-11 12:10:52 -07:00
parent 5c705d42c2
commit 33cbf99ec4
4 changed files with 121 additions and 15 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@attic/noms",
"version": "7.6.1",
"version": "7.6.2",
"main": "dist/commonjs/noms.js",
"jsnext:main": "dist/es6/noms.js",
"dependencies": {

16
js/src/describe-type.js Normal file
View File

@@ -0,0 +1,16 @@
// @flow
import {ValueBase} from './value.js';
export default function describeType(v: any): string {
const t = typeof v;
if (t === 'object') {
if (v === null) {
return 'null';
}
if (v instanceof ValueBase) {
return v.type.describe();
}
}
return t;
}

View File

@@ -9,13 +9,28 @@ import RefValue from './ref-value.js';
import {newStruct} from './struct.js';
import type {NomsKind} from './noms-kind.js';
import {encodeNomsValue, JsonArrayWriter} from './encode.js';
import {Field, makeCompoundType, makeEnumType, makePrimitiveType, makeStructType, makeType, Type,}
from './type.js';
import {
blobType,
boolType,
Field,
float64Type,
int8Type,
makeCompoundType,
makeEnumType,
makeListType,
makeMapType,
makePrimitiveType,
makeSetType,
makeStructType,
makeType,
stringType,
Type,
} from './type.js';
import {IndexedMetaSequence, MetaTuple} from './meta-sequence.js';
import {Kind} from './noms-kind.js';
import {ListLeafSequence, NomsList} from './list.js';
import {MapLeafSequence, NomsMap} from './map.js';
import {NomsSet, SetLeafSequence} from './set.js';
import {newList, ListLeafSequence, NomsList} from './list.js';
import {newMap, MapLeafSequence, NomsMap} from './map.js';
import {newSet, NomsSet, SetLeafSequence} from './set.js';
import {Package, registerPackage} from './package.js';
import {newBlob} from './blob.js';
import DataStore from './data-store.js';
@@ -434,4 +449,60 @@ suite('Encode', () => {
assert.deepEqual([Kind.Ref, Kind.Blob, ref.toString()], w.array);
});
test('type errors', async () => {
const ds = new DataStore(new MemoryStore());
const w = new JsonArrayWriter(ds);
const test = (et, at, t, v) => {
try {
w.writeTopLevel(t, v);
} catch (ex) {
assert.equal(ex.message, `Failed to write ${et}. Invalid type: ${at}`);
return;
}
assert.ok(false, `Expected error, 'Failed to write ${et}. Invalid type: ${at}' but Got none`);
};
test('Int8', 'string', int8Type, 'hi');
test('Float64', 'string', float64Type, 'hi');
test('Bool', 'string', boolType, 'hi');
test('Blob', 'string', blobType, 'hi');
test('String', 'number', stringType, 42);
test('Bool', 'number', boolType, 42);
test('Blob', 'number', blobType, 42);
test('Int8', 'boolean', int8Type, true);
test('Float64', 'boolean', float64Type, true);
test('String', 'boolean', stringType, true);
test('Blob', 'boolean', blobType, true);
const blob = await newBlob(new Uint8Array([0, 1]));
test('Int8', 'Blob', int8Type, blob);
test('Float64', 'Blob', float64Type, blob);
test('String', 'Blob', stringType, blob);
test('Bool', 'Blob', boolType, blob);
const list = await newList([0, 1], makeListType(int8Type));
test('Int8', 'List<Int8>', int8Type, list);
test('Float64', 'List<Int8>', float64Type, list);
test('String', 'List<Int8>', stringType, list);
test('Bool', 'List<Int8>', boolType, list);
test('Blob', 'List<Int8>', blobType, list);
const map = await newMap(['zero', 1], makeMapType(stringType, int8Type));
test('Int8', 'Map<String, Int8>', int8Type, map);
test('Float64', 'Map<String, Int8>', float64Type, map);
test('String', 'Map<String, Int8>', stringType, map);
test('Bool', 'Map<String, Int8>', boolType, map);
test('Blob', 'Map<String, Int8>', blobType, map);
const set = await newSet([0, 1], makeSetType(int8Type));
test('Int8', 'Set<Int8>', int8Type, set);
test('Float64', 'Set<Int8>', float64Type, set);
test('String', 'Set<Int8>', stringType, set);
test('Bool', 'Set<Int8>', boolType, set);
test('Blob', 'Set<Int8>', blobType, set);
});
});

View File

@@ -18,6 +18,7 @@ import {NomsSet, SetLeafSequence} from './set.js';
import {Sequence} from './sequence.js';
import {setEncodeNomsValue} from './get-ref.js';
import {NomsBlob, BlobLeafSequence} from './blob.js';
import describeType from './describe-type.js';
const typedTag = 't ';
@@ -118,7 +119,8 @@ export class JsonArrayWriter {
writeValue(v: any, t: Type, pkg: ?Package) {
switch (t.kind) {
case Kind.Blob:
invariant(v instanceof NomsBlob || v instanceof Sequence);
invariant(v instanceof NomsBlob || v instanceof Sequence,
`Failed to write Blob. Invalid type: ${describeType(v)}`);
const sequence: Sequence = v instanceof NomsBlob ? v.sequence : v;
if (this.maybeWriteMetaSequence(sequence, t, pkg)) {
@@ -129,11 +131,18 @@ export class JsonArrayWriter {
this.writeBlob(sequence);
break;
case Kind.Bool:
invariant(typeof v === 'boolean', `Failed to write Bool. Invalid type: ${describeType(v)}`);
this.write(v);
break;
case Kind.String:
invariant(typeof v === 'string',
`Failed to write String. Invalid type: ${describeType(v)}`);
this.write(v);
break;
case Kind.Float32:
case Kind.Float64:
invariant(typeof v === 'number',
`Failed to write ${t.describe()}. Invalid type: ${describeType(v)}`);
this.writeFloat(v); // TODO: Verify value fits in type
break;
case Kind.Uint8:
@@ -144,10 +153,13 @@ export class JsonArrayWriter {
case Kind.Int16:
case Kind.Int32:
case Kind.Int64:
invariant(typeof v === 'number',
`Failed to write ${t.describe()}. Invalid type: ${describeType(v)}`);
this.writeInt(v); // TODO: Verify value fits in type
break;
case Kind.List: {
invariant(v instanceof NomsList || v instanceof Sequence);
invariant(v instanceof NomsList || v instanceof Sequence,
`Failed to write List. Invalid type: ${describeType(v)}`);
const sequence: Sequence = v instanceof NomsList ? v.sequence : v;
if (this.maybeWriteMetaSequence(sequence, t, pkg)) {
@@ -162,7 +174,8 @@ export class JsonArrayWriter {
break;
}
case Kind.Map: {
invariant(v instanceof NomsMap || v instanceof Sequence);
invariant(v instanceof NomsMap || v instanceof Sequence,
`Failed to write Map. Invalid type: ${describeType(v)}`);
const sequence: Sequence = v instanceof NomsMap ? v.sequence : v;
if (this.maybeWriteMetaSequence(sequence, t, pkg)) {
@@ -181,7 +194,8 @@ export class JsonArrayWriter {
break;
}
case Kind.Package: {
invariant(v instanceof Package);
invariant(v instanceof Package,
`Failed to write Package. Invalid type: ${describeType(v)}`);
const ptr = makePrimitiveType(Kind.Type);
const w2 = new JsonArrayWriter(this._ds);
v.types.forEach(type => w2.writeValue(type, ptr, pkg));
@@ -192,12 +206,14 @@ export class JsonArrayWriter {
break;
}
case Kind.Ref: {
invariant(v instanceof RefValue);
invariant(v instanceof RefValue,
`Failed to write Ref. Invalid type: ${describeType(v)}`);
this.writeRef(v.targetRef);
break;
}
case Kind.Set: {
invariant(v instanceof NomsSet || v instanceof Sequence);
invariant(v instanceof NomsSet || v instanceof Sequence,
`Failed to write Set. Invalid type: ${describeType(v)}`);
const sequence: Sequence = v instanceof NomsSet ? v.sequence : v;
if (this.maybeWriteMetaSequence(sequence, t, pkg)) {
@@ -216,7 +232,8 @@ export class JsonArrayWriter {
break;
}
case Kind.Type: {
invariant(v instanceof Type);
invariant(v instanceof Type,
`Failed to write Type. Invalid type: ${describeType(v)}`);
this.writeTypeAsValue(v);
break;
}
@@ -310,11 +327,13 @@ export class JsonArrayWriter {
const typeDef = pkg.types[t.ordinal];
switch (typeDef.kind) {
case Kind.Enum:
invariant(typeof v === 'number');
invariant(typeof v === 'number',
`Failed to write ${typeDef.describe()}. Invalid type: ${describeType(v)}`);
this.writeEnum(v);
break;
case Kind.Struct: {
invariant(v instanceof Struct);
invariant(v instanceof Struct,
`Failed to write ${typeDef.describe()}. Invalid type: ${describeType(v)}`);
this.writeStruct(v, t, typeDef, pkg);
break;
}