Skip to content

Commit

Permalink
some tests for the schema
Browse files Browse the repository at this point in the history
  • Loading branch information
moodysalem committed Jun 11, 2020
1 parent b1db9d0 commit 07365d3
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 40 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# @uniswap/token-lists

This package includes a JSON schema for token lists, and TypeScript for
validating token lists against the schema.
This package includes a JSON schema for token lists, and TypeScript utilities for working with token lists.

## Validating token lists

This package does not include token list validation. You can easily do this by including a library such as
[ajv](https://ajv.js.org/) to perform the validation against the JSON schema.

## Local Development

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
},
"module": "dist/token-lists.esm.js",
"devDependencies": {
"ajv": "^6.12.2",
"husky": "^4.2.5",
"tsdx": "^0.13.2",
"tslib": "^2.0.0",
Expand Down
7 changes: 3 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { TokenList } from 'types';
import schema from './tokenlist.schema.json';

export * from './types';
export * from './isVersionUpdate';

export function validateTokenList(list: unknown): list is TokenList {
return typeof list === 'object';
}
export { schema };
18 changes: 18 additions & 0 deletions src/isVersionUpdate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Version } from './types';

/**
* Returns true if versionB is an update over versionA
*/
export function isVersionUpdate(versionA: Version, versionB: Version): boolean {
if (versionB.major > versionA.major) {
return true;
}
if (versionB.major < versionA.major) {
return false;
}
if (versionB.minor > versionA.minor) {
return true;
}
if (versionB.minor < versionA.minor) return false;
return versionB.patch > versionA.patch;
}
66 changes: 42 additions & 24 deletions src/tokenlist.schema.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
{
"$schema": "https://json-schema.org/draft-07/schema#",
"$id": "https://uniswap.org/token-list.schema.json",
"title": "Uniswap Token List",
"description": "A list of tokens compatible with the Uniswap Interface",
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://uniswap.org/token-lists.schema.json",
"title": "Uniswap Token Lists",
"description": "Schema for lists of tokens compatible with the Uniswap Interface",
"$ref": "#/definitions/TokenList",
"definitions": {
"Version": {
"type": "object",
"description": "A version number for a piece of data, useful for change detection",
"description": "A version number for a piece of data, used in list change detection",
"properties": {
"major": {
"type": "integer",
"description": "The major version of the list. Recommend incrementing major version when tokens are removed.",
"minimum": 1
"description": "The major version of the list. Must be incremented when tokens are removed from the list or token addresses are changed.",
"minimum": 0
},
"minor": {
"type": "integer",
"description": "The minor version of the list. Recommend incrementing minor version when tokens are added.",
"description": "The minor version of the list. Must be incremented when tokens are added to the list.",
"minimum": 0
},
"patch": {
"type": "integer",
"description": "The patch version of the list. Recommend incrementing for changes to tokens.",
"description": "The patch version of the list. Must be incremented for any changes to the list.",
"minimum": 0
}
},
Expand All @@ -31,9 +31,16 @@
"patch"
]
},
"TagIdentifier": {
"type": "string",
"description": "The identifier of a tag",
"minLength": 1,
"maxLength": 10,
"pattern": "^[\\w]+$"
},
"TagDefinition": {
"type": "object",
"description": "Tag definition",
"description": "Definition of a tag that can be associated with a token",
"properties": {
"name": {
"type": "string",
Expand All @@ -44,7 +51,7 @@
},
"description": {
"type": "string",
"description": "The user-friendly description of the tag",
"description": "A user-friendly description of the tag",
"pattern": "^[\\s\\w\\.]+$",
"minLength": 1,
"maxLength": 200
Expand All @@ -57,40 +64,49 @@
},
"TokenInfo": {
"type": "object",
"description": "Information about a single token",
"description": "Information about a single token on the token list",
"properties": {
"chainId": {
"type": "integer",
"description": "The chain ID of the chain where this token lives",
"description": "The chain ID of the Ethereum network where this token is deployed",
"minimum": 1
},
"address": {
"type": "string",
"description": "The address of the token",
"description": "The address of the token on the given chain ID",
"pattern": "^0x[a-fA-F0-9]{40}$"
},
"decimals": {
"type": "integer",
"description": "How many decimals the token has. Defaults to on-chain data",
"description": "The number of decimals for the token balance; if not set, defaults to on-chain data",
"minimum": 0,
"maximum": 78
},
"name": {
"type": "string",
"description": "The name of the token. Defaults to on-chain data",
"minLength": 1
"description": "The name of the token; if not set, defaults to on-chain data",
"minLength": 1,
"pattern": "^[\\s\\w]+$"
},
"symbol": {
"type": "string",
"description": "The symbol for the token. Defaults to on-chain data",
"description": "The symbol for the token; must be alphanumeric; if not set, defaults to on-chain data",
"pattern": "^[a-zA-Z0-9]+$",
"minLength": 1,
"maxLength": 20
},
"logoUrl": {
"type": "string",
"description": "A URL to the token description",
"description": "A URL to the token logo asset; if not set, interface will attempt to find a logo based on the address",
"pattern": "^(https|ipfs|ipns)://.+$"
},
"tags": {
"type": "array",
"description": "An array of tag identifiers associated with the token",
"items": {
"$ref": "#/definitions/TagIdentifier"
},
"maxLength": 10
}
},
"required": [
Expand All @@ -105,11 +121,13 @@
"type": "string",
"description": "The name of the token list",
"minLength": 1,
"maxLength": 20
"maxLength": 20,
"pattern": "^[\\w\\s]+$"
},
"timestamp": {
"type": "integer",
"description": "The epoch seconds timestamp of the list version"
"description": "The epoch seconds timestamp of the list version; must be after June 11, 2020",
"minimum": 1591833600
},
"version": {
"$ref": "#/definitions/Version"
Expand All @@ -125,7 +143,7 @@
},
"keywords": {
"type": "array",
"description": "Keywords associated with the contents of the list",
"description": "Keywords associated with the contents of the list; may be used in list discoverability",
"items": {
"type": "string",
"minLength": 1,
Expand All @@ -136,9 +154,9 @@
},
"tags": {
"type": "object",
"description": "A mapping of tag identifiers to their name and description.",
"description": "A mapping of tag identifiers to their name and description; tag identifier keys are slugs",
"propertyNames": {
"pattern": "^\\d+$"
"$ref": "#/definitions/TagIdentifier"
},
"additionalProperties": {
"$ref": "#/definitions/TagDefinition"
Expand Down
84 changes: 84 additions & 0 deletions test/isVersionUpdate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { isVersionUpdate } from '../src';

describe('#isVersionUpdate', () => {
it('major version increase', () => {
expect(
isVersionUpdate(
{ major: 1, minor: 0, patch: 0 },
{ major: 2, minor: 0, patch: 0 }
)
).toEqual(true);
expect(
isVersionUpdate(
{ major: 1, minor: 0, patch: 0 },
{ major: 1, minor: 0, patch: 0 }
)
).toEqual(false);
expect(
isVersionUpdate(
{ major: 1, minor: 0, patch: 0 },
{ major: 0, minor: 0, patch: 0 }
)
).toEqual(false);
expect(
isVersionUpdate(
{ major: 1, minor: 1, patch: 0 },
{ major: 2, minor: 0, patch: 0 }
)
).toEqual(true);
});

it('minor version increase', () => {
expect(
isVersionUpdate(
{ major: 1, minor: 0, patch: 0 },
{ major: 1, minor: 1, patch: 0 }
)
).toEqual(true);
expect(
isVersionUpdate(
{ major: 1, minor: 0, patch: 0 },
{ major: 1, minor: 0, patch: 0 }
)
).toEqual(false);
expect(
isVersionUpdate(
{ major: 1, minor: 1, patch: 0 },
{ major: 1, minor: 0, patch: 0 }
)
).toEqual(false);
expect(
isVersionUpdate(
{ major: 1, minor: 1, patch: 1 },
{ major: 1, minor: 2, patch: 0 }
)
).toEqual(true);
});

it('patch version', () => {
expect(
isVersionUpdate(
{ major: 1, minor: 0, patch: 0 },
{ major: 1, minor: 1, patch: 0 }
)
).toEqual(true);
expect(
isVersionUpdate(
{ major: 1, minor: 0, patch: 0 },
{ major: 1, minor: 0, patch: 0 }
)
).toEqual(false);
expect(
isVersionUpdate(
{ major: 1, minor: 1, patch: 0 },
{ major: 1, minor: 0, patch: 0 }
)
).toEqual(false);
expect(
isVersionUpdate(
{ major: 1, minor: 1, patch: 1 },
{ major: 1, minor: 2, patch: 0 }
)
).toEqual(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"description": "blah blah"
}
},
"timestamp": 100,
"timestamp": 1591897594,
"tokens": [
{
"name": "blah",
Expand All @@ -19,8 +19,8 @@
}
],
"version": {
"major": 1,
"major": 0,
"minor": 0,
"patch": 0
"patch": 1
}
}
48 changes: 48 additions & 0 deletions test/tokenlist.schema.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Ajv from 'ajv';
import { schema } from '../src';
import example from './testschemas/example.tokenlist.json';
const ajv = new Ajv({ allErrors: true });

const validator = ajv.compile(schema);
describe('schema', () => {
it('is valid', () => {
expect(ajv.validateSchema(schema)).toEqual(true);
});
it('requires name, timestamp, version, tokens', () => {
expect(validator({})).toEqual(false);
expect(validator.errors).toEqual([
{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'name' },
message: "should have required property 'name'",
},
{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'timestamp' },
message: "should have required property 'timestamp'",
},
{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'version' },
message: "should have required property 'version'",
},
{
keyword: 'required',
dataPath: '',
schemaPath: '#/required',
params: { missingProperty: 'tokens' },
message: "should have required property 'tokens'",
},
]);
});
it('works for example schema', () => {
expect(validator(example)).toEqual(true);
expect(validator.errors).toBeNull();
});
});
7 changes: 0 additions & 7 deletions test/validateTokenList.test.ts

This file was deleted.

1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
Expand Down

0 comments on commit 07365d3

Please sign in to comment.