mirror of
https://github.com/Cameri/nostream.git
synced 2025-09-27 12:56:15 +02:00
test: add rune-like, restriction, and alternative
This commit is contained in:
@@ -5,7 +5,7 @@ import { CanonicalEvent, Event } from '../@types/event'
|
|||||||
import { EventKinds, EventTags } from '../constants/base'
|
import { EventKinds, EventTags } from '../constants/base'
|
||||||
import { fromBuffer } from './transform'
|
import { fromBuffer } from './transform'
|
||||||
import { isGenericTagQuery } from './filter'
|
import { isGenericTagQuery } from './filter'
|
||||||
import { Rune } from './runes'
|
import { RuneLike } from './runes/rune-like'
|
||||||
import { SubscriptionFilter } from '../@types/subscription'
|
import { SubscriptionFilter } from '../@types/subscription'
|
||||||
|
|
||||||
export const serializeEvent = (event: Partial<Event>): CanonicalEvent => [
|
export const serializeEvent = (event: Partial<Event>): CanonicalEvent => [
|
||||||
@@ -128,7 +128,7 @@ export const isDelegatedEventValid = async (event: Event): Promise<boolean> => {
|
|||||||
|
|
||||||
let result: boolean
|
let result: boolean
|
||||||
try {
|
try {
|
||||||
[result] = Rune.from(delegation[2]).test(runifiedEvent)
|
[result] = RuneLike.from(delegation[2]).test(runifiedEvent)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
result = false
|
result = false
|
||||||
}
|
}
|
||||||
|
@@ -125,91 +125,4 @@ export class Alternative {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Restriction {
|
|
||||||
public constructor(
|
|
||||||
private readonly alternatives: Alternative[]
|
|
||||||
) {
|
|
||||||
if (!alternatives.length) {
|
|
||||||
throw new Error('Restriction must have some alternatives')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public test(values: Record<string, any>): string | undefined {
|
|
||||||
const reasons: string[] = []
|
|
||||||
for (const alternative of this.alternatives) {
|
|
||||||
const reason = alternative.test(values)
|
|
||||||
if (typeof reason === 'undefined') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reasons.push(reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
return reasons.join(' AND ')
|
|
||||||
}
|
|
||||||
|
|
||||||
public encode(): string {
|
|
||||||
return this.alternatives.map((alternative) => alternative.encode()).join('|')
|
|
||||||
}
|
|
||||||
|
|
||||||
public static decode(encodedStr: string): [Restriction, string] {
|
|
||||||
let encStr = encodedStr
|
|
||||||
let alternative: Alternative
|
|
||||||
const alternatives: Alternative[] = []
|
|
||||||
while (encStr.length) {
|
|
||||||
if (encStr.startsWith('&')) {
|
|
||||||
encStr = encStr.slice(1)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
[alternative, encStr] = Alternative.decode(encStr)
|
|
||||||
|
|
||||||
alternatives.push(alternative)
|
|
||||||
}
|
|
||||||
|
|
||||||
return [new Restriction(alternatives), encStr]
|
|
||||||
}
|
|
||||||
|
|
||||||
public static from(encodedStr: string): Restriction {
|
|
||||||
const [restriction, remainder] = Restriction.decode(encodedStr.replace(/\s+/g, ''))
|
|
||||||
|
|
||||||
if (remainder.length) {
|
|
||||||
throw new Error(`Restriction had extra characters at end: ${remainder}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return restriction
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Rune {
|
|
||||||
public constructor(
|
|
||||||
private readonly restrictions: Restriction[] = []
|
|
||||||
) { }
|
|
||||||
|
|
||||||
public test(values: Record<string, string | string[]>): [boolean, string] {
|
|
||||||
for (const restriction of this.restrictions) {
|
|
||||||
const reasons = restriction.test(values)
|
|
||||||
if (typeof reasons !== 'undefined') {
|
|
||||||
return [false, reasons]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [true, '']
|
|
||||||
}
|
|
||||||
|
|
||||||
public encode() {
|
|
||||||
return this.restrictions.map((restriction) => restriction.encode()).join('&')
|
|
||||||
}
|
|
||||||
|
|
||||||
public static from(encodedStr: string): Rune {
|
|
||||||
const restrictions: Restriction[] = []
|
|
||||||
let restriction: Restriction
|
|
||||||
let encStr = encodedStr.replace(/\s+/g, '')
|
|
||||||
|
|
||||||
while (encStr.length) {
|
|
||||||
[restriction, encStr] = Restriction.decode(encStr)
|
|
||||||
restrictions.push(restriction)
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Rune(restrictions)
|
|
||||||
}
|
|
||||||
}
|
|
47
src/utils/runes/restriction.ts
Normal file
47
src/utils/runes/restriction.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { Alternative } from './alternative'
|
||||||
|
|
||||||
|
|
||||||
|
export class Restriction {
|
||||||
|
public constructor(
|
||||||
|
private readonly alternatives: Alternative[]
|
||||||
|
) {
|
||||||
|
if (!alternatives.length) {
|
||||||
|
throw new Error('Restriction must have some alternatives')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public test(values: Record<string, any>): string | undefined {
|
||||||
|
const reasons: string[] = []
|
||||||
|
for (const alternative of this.alternatives) {
|
||||||
|
const reason = alternative.test(values)
|
||||||
|
if (typeof reason === 'undefined') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reasons.push(reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reasons.join(' AND ')
|
||||||
|
}
|
||||||
|
|
||||||
|
public encode(): string {
|
||||||
|
return this.alternatives.map((alternative) => alternative.encode()).join('|')
|
||||||
|
}
|
||||||
|
|
||||||
|
public static decode(encodedStr: string): [Restriction, string] {
|
||||||
|
let encStr = encodedStr
|
||||||
|
let alternative: Alternative
|
||||||
|
const alternatives: Alternative[] = []
|
||||||
|
while (encStr.length) {
|
||||||
|
if (encStr.startsWith('&')) {
|
||||||
|
encStr = encStr.slice(1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
[alternative, encStr] = Alternative.decode(encStr)
|
||||||
|
|
||||||
|
alternatives.push(alternative)
|
||||||
|
}
|
||||||
|
|
||||||
|
return [new Restriction(alternatives), encStr]
|
||||||
|
}
|
||||||
|
}
|
36
src/utils/runes/rune-like.ts
Normal file
36
src/utils/runes/rune-like.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { Restriction } from './restriction'
|
||||||
|
|
||||||
|
|
||||||
|
export class RuneLike {
|
||||||
|
public constructor(
|
||||||
|
private readonly restrictions: Restriction[] = []
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public test(values: Record<string, unknown>): [boolean, string] {
|
||||||
|
for (const restriction of this.restrictions) {
|
||||||
|
const reasons = restriction.test(values)
|
||||||
|
if (typeof reasons !== 'undefined') {
|
||||||
|
return [false, reasons]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [true, '']
|
||||||
|
}
|
||||||
|
|
||||||
|
public encode() {
|
||||||
|
return this.restrictions.map((restriction) => restriction.encode()).join('&')
|
||||||
|
}
|
||||||
|
|
||||||
|
public static from(encodedStr: string): RuneLike {
|
||||||
|
const restrictions: Restriction[] = []
|
||||||
|
let restriction: Restriction
|
||||||
|
let encStr = encodedStr.replace(/\s+/g, '')
|
||||||
|
|
||||||
|
while (encStr.length) {
|
||||||
|
[restriction, encStr] = Restriction.decode(encStr)
|
||||||
|
restrictions.push(restriction)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RuneLike(restrictions)
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
|
|
||||||
import { Alternative } from '../../../src/utils/runes'
|
import { Alternative } from '../../../../src/utils/runes/alternative'
|
||||||
|
|
||||||
describe('Alternative', () => {
|
describe('Alternative', () => {
|
||||||
describe('constructor', () => {
|
describe('constructor', () => {
|
132
test/unit/utils/runes/restriction.spec.ts
Normal file
132
test/unit/utils/runes/restriction.spec.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import { expect } from 'chai'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
import { Alternative } from '../../../../src/utils/runes/alternative'
|
||||||
|
import { Restriction } from '../../../../src/utils/runes/restriction'
|
||||||
|
|
||||||
|
describe('Restriction', () => {
|
||||||
|
describe('constructor', () => {
|
||||||
|
it('throws if given alternatives list is empty', () => {
|
||||||
|
expect(() => new Restriction([])).to.throw(Error, 'Restriction must have some alternatives')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('test', () => {
|
||||||
|
it('returns undefined given 1 true alternative', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const alternatives: Alternative[] = [
|
||||||
|
{ test: sinon.fake.returns(undefined) },
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(new Restriction(alternatives).test(values)).to.be.undefined
|
||||||
|
|
||||||
|
expect(alternatives[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns undefined given 2 true alternative', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const alternatives: Alternative[] = [
|
||||||
|
{ test: sinon.fake.returns(undefined) },
|
||||||
|
{ test: sinon.fake.returns(undefined) },
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(new Restriction(alternatives).test(values)).to.be.undefined
|
||||||
|
|
||||||
|
expect(alternatives[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
expect(alternatives[1].test).not.to.have.been.called
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns undefined given 1 true and 1 false alternative', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const alternatives: Alternative[] = [
|
||||||
|
{ test: sinon.fake.returns(undefined) },
|
||||||
|
{ test: sinon.fake.returns('reason') },
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(new Restriction(alternatives).test(values)).to.be.undefined
|
||||||
|
|
||||||
|
expect(alternatives[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
expect(alternatives[1].test).not.to.have.been.called
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns reason given 1 false alternative', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const alternatives: Alternative[] = [
|
||||||
|
{ test: sinon.fake.returns('reason') },
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(new Restriction(alternatives).test(values)).to.equal('reason')
|
||||||
|
|
||||||
|
expect(alternatives[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns undefined given 1 false and 1 true alternative', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const alternatives: Alternative[] = [
|
||||||
|
{ test: sinon.fake.returns('reason') },
|
||||||
|
{ test: sinon.fake.returns(undefined) },
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(new Restriction(alternatives).test(values)).to.be.undefined
|
||||||
|
|
||||||
|
expect(alternatives[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
expect(alternatives[1].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns reasons given 2 false alternatives', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const alternatives: Alternative[] = [
|
||||||
|
{ test: sinon.fake.returns('reason 1') },
|
||||||
|
{ test: sinon.fake.returns('reason 2') },
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(new Restriction(alternatives).test(values)).to.equal('reason 1 AND reason 2')
|
||||||
|
|
||||||
|
expect(alternatives[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
expect(alternatives[1].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('encode', () => {
|
||||||
|
it('returns encoded restriction with 1 alternative', () => {
|
||||||
|
const alternatives: Alternative[] = [
|
||||||
|
{ encode: sinon.fake.returns('a=1') },
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(new Restriction(alternatives).encode()).to.equal('a=1')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
it('returns encoded restrictions with 2 alternatives', () => {
|
||||||
|
const alternatives: Alternative[] = [
|
||||||
|
{ encode: sinon.fake.returns('a=1') },
|
||||||
|
{ encode: sinon.fake.returns('b=2') },
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(new Restriction(alternatives).encode()).to.equal('a=1|b=2')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('decode', () => {
|
||||||
|
it('returns encoded restriction given 1 alternative', () => {
|
||||||
|
const [restriction, remainder] = Restriction.decode('a=1')
|
||||||
|
|
||||||
|
expect(restriction.encode()).to.equal('a=1')
|
||||||
|
expect(remainder).to.be.empty
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns encoded restriction given 2 alternatives', () => {
|
||||||
|
const [restriction, remainder] = Restriction.decode('a=1|b=2')
|
||||||
|
|
||||||
|
expect(restriction.encode()).to.equal('a=1|b=2')
|
||||||
|
expect(remainder).to.be.empty
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns encoded restriction given 2 alternatives and another restriction', () => {
|
||||||
|
const [restriction, remainder] = Restriction.decode('a=1|b=2&c=1')
|
||||||
|
|
||||||
|
expect(restriction.encode()).to.equal('a=1|b=2')
|
||||||
|
expect(remainder).to.equal('c=1')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
173
test/unit/utils/runes/rune-like.spec.ts
Normal file
173
test/unit/utils/runes/rune-like.spec.ts
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
import { expect } from 'chai'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
import { Alternative } from '../../../../src/utils/runes/alternative'
|
||||||
|
import { Restriction } from '../../../../src/utils/runes/restriction'
|
||||||
|
import { RuneLike } from '../../../../src/utils/runes/rune-like'
|
||||||
|
|
||||||
|
describe('RuneLike', () => {
|
||||||
|
describe('test', () => {
|
||||||
|
it('returns true if 1 restriction is true', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const restrictions: Restriction[] = [
|
||||||
|
{ test: sinon.fake.returns(undefined) } as any,
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(new RuneLike(restrictions).test(values)).to.deep.equal([true, ''])
|
||||||
|
expect(restrictions[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns false and reason if 1 restriction is false', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const restrictions: Restriction[] = [
|
||||||
|
{ test: sinon.fake.returns('reason') } as any,
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(new RuneLike(restrictions).test(values)).to.deep.equal([false, 'reason'])
|
||||||
|
expect(restrictions[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns false if 1 restriction is true and 1 is false', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const restrictions: Restriction[] = [
|
||||||
|
{ test: sinon.fake.returns(undefined) } as any,
|
||||||
|
{ test: sinon.fake.returns('reason 2') } as any,
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(new RuneLike(restrictions).test(values)).to.deep.equal([false, 'reason 2'])
|
||||||
|
expect(restrictions[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
expect(restrictions[1].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns false if 1 restriction is false and 1 is true', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const restrictions: Restriction[] = [
|
||||||
|
{ test: sinon.fake.returns('reason 1') } as any,
|
||||||
|
{ test: sinon.fake.returns(undefined) } as any,
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(new RuneLike(restrictions).test(values)).to.deep.equal([false, 'reason 1'])
|
||||||
|
expect(restrictions[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
expect(restrictions[1].test).not.to.have.been.called
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns false if 2 restrictions are false', () => {
|
||||||
|
const values = { a: 1 }
|
||||||
|
const restrictions: Restriction[] = [
|
||||||
|
{ test: sinon.fake.returns('reason 1') } as any,
|
||||||
|
{ test: sinon.fake.returns('reason 2') } as any,
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(new RuneLike(restrictions).test(values)).to.deep.equal([false, 'reason 1'])
|
||||||
|
expect(restrictions[0].test).to.have.been.calledOnceWithExactly(values)
|
||||||
|
expect(restrictions[1].test).not.to.have.been.called
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('encode', () => {
|
||||||
|
it('encodes 1 restriction', () => {
|
||||||
|
const restrictions: Restriction[] = [
|
||||||
|
{ encode: sinon.fake.returns('a=1') },
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(new RuneLike(restrictions).encode()).to.equal('a=1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('encodes 2 restrictions', () => {
|
||||||
|
const restrictions: Restriction[] = [
|
||||||
|
{ encode: sinon.fake.returns('a=1') },
|
||||||
|
{ encode: sinon.fake.returns('b=2') },
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(new RuneLike(restrictions).encode()).to.equal('a=1&b=2')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('from', () => {
|
||||||
|
let restrictionDecodeStub: sinon.SinonStub
|
||||||
|
beforeEach(() => {
|
||||||
|
restrictionDecodeStub = sinon.stub(Restriction, 'decode')
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
restrictionDecodeStub.restore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns rune-like given restrictions a=1', () => {
|
||||||
|
restrictionDecodeStub.withArgs('a=1').returns([
|
||||||
|
new Restriction([
|
||||||
|
new Alternative('a', '=', '1'),
|
||||||
|
]),
|
||||||
|
'',
|
||||||
|
])
|
||||||
|
const runeLike = RuneLike.from('a=1')
|
||||||
|
|
||||||
|
expect(runeLike).to.be.an.instanceOf(RuneLike)
|
||||||
|
|
||||||
|
expect(restrictionDecodeStub.firstCall).to.have.been.calledWithExactly('a=1')
|
||||||
|
expect(runeLike.encode()).to.equal('a=1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns rune-like given restrictions a=1|b=2', () => {
|
||||||
|
restrictionDecodeStub.withArgs('a=1|b=2').returns([
|
||||||
|
new Restriction([
|
||||||
|
new Alternative('a', '=', '1'),
|
||||||
|
new Alternative('b', '=', '2'),
|
||||||
|
]),
|
||||||
|
'',
|
||||||
|
])
|
||||||
|
const runeLike = RuneLike.from('a=1|b=2')
|
||||||
|
|
||||||
|
expect(runeLike).to.be.an.instanceOf(RuneLike)
|
||||||
|
|
||||||
|
expect(restrictionDecodeStub.firstCall).to.have.been.calledWithExactly('a=1|b=2')
|
||||||
|
expect(runeLike.encode()).to.equal('a=1|b=2')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns rune-like given restrictions a=1|b=2&c=3', () => {
|
||||||
|
restrictionDecodeStub.withArgs('a=1|b=2&c=3').returns([
|
||||||
|
new Restriction([
|
||||||
|
new Alternative('a', '=', '1'),
|
||||||
|
new Alternative('b', '=', '2'),
|
||||||
|
]),
|
||||||
|
'&c=3',
|
||||||
|
])
|
||||||
|
restrictionDecodeStub.withArgs('&c=3').returns([
|
||||||
|
new Restriction([
|
||||||
|
new Alternative('c', '=', '3'),
|
||||||
|
]),
|
||||||
|
'',
|
||||||
|
])
|
||||||
|
const runeLike = RuneLike.from('a=1|b=2&c=3')
|
||||||
|
|
||||||
|
expect(runeLike).to.be.an.instanceOf(RuneLike)
|
||||||
|
|
||||||
|
expect(restrictionDecodeStub.firstCall).to.have.been.calledWithExactly('a=1|b=2&c=3')
|
||||||
|
expect(restrictionDecodeStub.secondCall).to.have.been.calledWithExactly('&c=3')
|
||||||
|
expect(runeLike.encode()).to.equal('a=1|b=2&c=3')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns rune-like given restrictions with spaces a = 1 | b = 2 & c = 3', () => {
|
||||||
|
restrictionDecodeStub.withArgs('a=1|b=2&c=3').returns([
|
||||||
|
new Restriction([
|
||||||
|
new Alternative('a', '=', '1'),
|
||||||
|
new Alternative('b', '=', '2'),
|
||||||
|
]),
|
||||||
|
'&c=3',
|
||||||
|
])
|
||||||
|
restrictionDecodeStub.withArgs('&c=3').returns([
|
||||||
|
new Restriction([
|
||||||
|
new Alternative('c', '=', '3'),
|
||||||
|
]),
|
||||||
|
'',
|
||||||
|
])
|
||||||
|
const runeLike = RuneLike.from('a = 1 | b = 2 & c = 3')
|
||||||
|
|
||||||
|
expect(runeLike).to.be.an.instanceOf(RuneLike)
|
||||||
|
|
||||||
|
expect(restrictionDecodeStub.firstCall).to.have.been.calledWithExactly('a=1|b=2&c=3')
|
||||||
|
expect(restrictionDecodeStub.secondCall).to.have.been.calledWithExactly('&c=3')
|
||||||
|
expect(runeLike.encode()).to.equal('a=1|b=2&c=3')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Reference in New Issue
Block a user