521 lines
13 KiB
JavaScript
521 lines
13 KiB
JavaScript
import { expect } from 'chai'
|
|
import { analyse, parseAll } from '../source/engine/traverse'
|
|
import { enrichRule } from '../source/engine/rules'
|
|
|
|
let stateSelector = () => null
|
|
|
|
describe('analyse', function() {
|
|
it('should directly return simple numerical values', function() {
|
|
let rule = { nom: 'startHere', formule: 3269 }
|
|
let rules = parseAll([rule].map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 3269)
|
|
})
|
|
|
|
it('should compute expressions combining constants', function() {
|
|
let rule = { nom: 'startHere', formule: '32 + 69' }
|
|
let rules = parseAll([rule].map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 101)
|
|
})
|
|
})
|
|
|
|
describe('analyse on raw rules', function() {
|
|
it('should handle direct referencing of a variable', function() {
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{ nom: 'startHere', formule: 'dix', espace: 'top' },
|
|
{ nom: 'dix', formule: 10, espace: 'top' }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 10)
|
|
})
|
|
|
|
it('should handle expressions referencing other rules', function() {
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{ nom: 'startHere', formule: '3259 + dix', espace: 'top' },
|
|
{ nom: 'dix', formule: 10, espace: 'top' }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 3269)
|
|
})
|
|
|
|
it('should handle applicability conditions', function() {
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{ nom: 'startHere', formule: '3259 + dix', espace: 'top' },
|
|
{ nom: 'dix', formule: 10, espace: 'top', 'non applicable si': 'vrai' },
|
|
{ nom: 'vrai', formule: '2 > 1', espace: 'top' }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 3259)
|
|
})
|
|
|
|
it('should handle comparisons', function() {
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{ nom: 'startHere', formule: '3259 > dix', espace: 'top' },
|
|
{ nom: 'dix', formule: 10, espace: 'top' }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', true)
|
|
})
|
|
|
|
/* TODO: make this pass
|
|
it('should handle applicability conditions', function() {
|
|
let rawRules = [
|
|
{nom: "startHere", formule: "3259 + dix", espace: "top"},
|
|
{nom: "dix", formule: 10, espace: "top", "non applicable si" : "vrai"},
|
|
{nom: "vrai", formule: "1", espace: "top"}],
|
|
rules = rawRules.map(enrichRule)
|
|
expect(analyse(rules,"startHere")(stateSelector).targets[0]).to.have.property('nodeValue',3259)
|
|
});
|
|
*/
|
|
})
|
|
|
|
describe('analyse with mecanisms', function() {
|
|
it('should handle n-way "or"', function() {
|
|
let rawRules = [
|
|
{
|
|
nom: 'startHere',
|
|
formule: { 'une de ces conditions': ['1 > 2', '1 > 0', '0 > 2'] }
|
|
}
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', true)
|
|
})
|
|
|
|
it('should handle n-way "and"', function() {
|
|
let rawRules = [
|
|
{
|
|
nom: 'startHere',
|
|
formule: { 'toutes ces conditions': ['1 > 2', '1 > 0', '0 > 2'] }
|
|
}
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', false)
|
|
})
|
|
|
|
it('should handle switch statements', function() {
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{
|
|
nom: 'startHere',
|
|
formule: {
|
|
'aiguillage numérique': {
|
|
'1 > dix': '1000%',
|
|
'3 < dix': '1100%',
|
|
'3 > dix': '1200%'
|
|
}
|
|
},
|
|
espace: 'top'
|
|
},
|
|
{ nom: 'dix', formule: 10, espace: 'top' }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 11)
|
|
})
|
|
|
|
it('should handle percentages', function() {
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{ nom: 'startHere', formule: '35%', espace: 'top' }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 0.35)
|
|
})
|
|
|
|
it('should handle sums', function() {
|
|
let rawRules = [
|
|
{ nom: 'startHere', formule: { somme: [3200, 'dix', 9] } },
|
|
{ nom: 'dix', formule: 10 }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 3219)
|
|
})
|
|
|
|
it('should handle multiplications', function() {
|
|
let rawRules = [
|
|
{
|
|
nom: 'startHere',
|
|
formule: {
|
|
multiplication: {
|
|
assiette: 3259,
|
|
plafond: 3200,
|
|
facteur: 1,
|
|
taux: 1.5
|
|
}
|
|
}
|
|
}
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 4800)
|
|
})
|
|
|
|
it('should handle components in multiplication', function() {
|
|
let rawRules = [
|
|
{
|
|
nom: 'startHere',
|
|
formule: {
|
|
multiplication: {
|
|
assiette: 3200,
|
|
composantes: [{ taux: 0.7 }, { taux: 0.8 }]
|
|
}
|
|
}
|
|
}
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 4800)
|
|
})
|
|
|
|
it('should apply a ceiling to the sum of components', function() {
|
|
let rawRules = [
|
|
{
|
|
nom: 'startHere',
|
|
formule: {
|
|
multiplication: {
|
|
assiette: 3259,
|
|
plafond: 3200,
|
|
composantes: [{ taux: 0.7 }, { taux: 0.8 }]
|
|
}
|
|
}
|
|
}
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 4800)
|
|
})
|
|
|
|
it('should handle progressive scales', function() {
|
|
let rawRules = [
|
|
{
|
|
nom: 'startHere',
|
|
formule: {
|
|
barème: {
|
|
assiette: 2008,
|
|
multiplicateur: 1000,
|
|
tranches: [
|
|
{ 'en-dessous de': 1, taux: 0.1 },
|
|
{ de: 1, à: 2, taux: 1.2 },
|
|
{ 'au-dessus de': 2, taux: 10 }
|
|
]
|
|
}
|
|
}
|
|
}
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 100 + 1200 + 80)
|
|
})
|
|
|
|
it('should handle progressive scales with components', function() {
|
|
let rawRules = [
|
|
{
|
|
nom: 'startHere',
|
|
formule: {
|
|
barème: {
|
|
assiette: 2008,
|
|
multiplicateur: 1000,
|
|
composantes: [
|
|
{
|
|
tranches: [
|
|
{ 'en-dessous de': 1, taux: 0.05 },
|
|
{ de: 1, à: 2, taux: 0.4 },
|
|
{ 'au-dessus de': 2, taux: 5 }
|
|
]
|
|
},
|
|
{
|
|
tranches: [
|
|
{ 'en-dessous de': 1, taux: 0.05 },
|
|
{ de: 1, à: 2, taux: 0.8 },
|
|
{ 'au-dessus de': 2, taux: 5 }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 100 + 1200 + 80)
|
|
})
|
|
|
|
it('should handle progressive scales with variations', function() {
|
|
let rawRules = [
|
|
{
|
|
nom: 'startHere',
|
|
formule: {
|
|
barème: {
|
|
assiette: 2008,
|
|
multiplicateur: 1000,
|
|
variations: [
|
|
{
|
|
si: '3 > 4',
|
|
alors: {
|
|
tranches: [
|
|
{ 'en-dessous de': 1, taux: 0.1 },
|
|
{ de: 1, à: 2, taux: 1.2 },
|
|
{ 'au-dessus de': 2, taux: 10 }
|
|
]
|
|
}
|
|
},
|
|
{
|
|
si: '3 > 2',
|
|
alors: {
|
|
tranches: [
|
|
{ 'en-dessous de': 1, taux: 0.1 },
|
|
{ de: 1, à: 2, taux: 1.8 },
|
|
{ 'au-dessus de': 2, taux: 10 }
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 100 + 1800 + 80)
|
|
})
|
|
|
|
it('should handle max', function() {
|
|
let rawRules = [
|
|
{ nom: 'startHere', formule: { 'le maximum de': [3200, 60, 9] } }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 3200)
|
|
})
|
|
|
|
it('should handle complements', function() {
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{
|
|
nom: 'startHere',
|
|
formule: { complément: { cible: 'dix', montant: 93 } },
|
|
espace: 'top'
|
|
},
|
|
{ nom: 'dix', formule: 17, espace: 'top' }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 93 - 17)
|
|
})
|
|
|
|
it('should handle components in complements', function() {
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{
|
|
nom: 'startHere',
|
|
formule: {
|
|
complément: {
|
|
cible: 'dix',
|
|
composantes: [{ montant: 93 }, { montant: 93 }]
|
|
}
|
|
},
|
|
espace: 'top'
|
|
},
|
|
{ nom: 'dix', formule: 17, espace: 'top' }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 2 * (93 - 17))
|
|
})
|
|
|
|
it('should handle filtering on components', function() {
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{ nom: 'startHere', espace: 'top', formule: 'composed (salarié)' },
|
|
{
|
|
nom: 'composed',
|
|
espace: 'top',
|
|
formule: {
|
|
barème: {
|
|
assiette: 2008,
|
|
multiplicateur: 1000,
|
|
composantes: [
|
|
{
|
|
tranches: [
|
|
{ 'en-dessous de': 1, taux: 0.05 },
|
|
{ de: 1, à: 2, taux: 0.4 },
|
|
{ 'au-dessus de': 2, taux: 5 }
|
|
],
|
|
attributs: { 'dû par': 'salarié' }
|
|
},
|
|
{
|
|
tranches: [
|
|
{ 'en-dessous de': 1, taux: 0.05 },
|
|
{ de: 1, à: 2, taux: 0.8 },
|
|
{ 'au-dessus de': 2, taux: 5 }
|
|
],
|
|
attributs: { 'dû par': 'employeur' }
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 50 + 400 + 40)
|
|
})
|
|
|
|
it('should compute consistent values', function() {
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{
|
|
nom: 'startHere',
|
|
espace: 'top',
|
|
formule: 'composed (salarié) + composed (employeur)'
|
|
},
|
|
{ nom: 'orHere', espace: 'top', formule: 'composed' },
|
|
{
|
|
nom: 'composed',
|
|
espace: 'top',
|
|
formule: {
|
|
barème: {
|
|
assiette: 2008,
|
|
multiplicateur: 1000,
|
|
composantes: [
|
|
{
|
|
tranches: [
|
|
{ 'en-dessous de': 1, taux: 0.05 },
|
|
{ de: 1, à: 2, taux: 0.4 },
|
|
{ 'au-dessus de': 2, taux: 5 }
|
|
],
|
|
attributs: { 'dû par': 'salarié' }
|
|
},
|
|
{
|
|
tranches: [
|
|
{ 'en-dessous de': 1, taux: 0.05 },
|
|
{ de: 1, à: 2, taux: 0.8 },
|
|
{ 'au-dessus de': 2, taux: 5 }
|
|
],
|
|
attributs: { 'dû par': 'employeur' }
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(analyse(rules, 'orHere')(stateSelector).targets[0]).to.have.property(
|
|
'nodeValue',
|
|
100 + 1200 + 80
|
|
)
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 100 + 1200 + 80)
|
|
})
|
|
|
|
it('should handle selection', function() {
|
|
let stateSelector = name => ({ 'top . code postal': '2' }[name])
|
|
let data = {
|
|
taux_versement_transport: [
|
|
{ codePostal: 1, aot: '2.5' },
|
|
{ codePostal: 2, smt: '1.5' }
|
|
]
|
|
}
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{
|
|
espace: 'top',
|
|
nom: 'startHere',
|
|
formule: {
|
|
sélection: {
|
|
données: 'startHere',
|
|
cherche: 'code postal',
|
|
dans: 'codePostal',
|
|
renvoie: 'smt'
|
|
}
|
|
},
|
|
données: 'taux_versement_transport'
|
|
},
|
|
{ espace: 'top', nom: 'code postal', format: 'nombre' }
|
|
],
|
|
rules = parseAll(rawRules.map(rule => enrichRule(rule, data)))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 0.015)
|
|
})
|
|
|
|
it('should handle failed selections', function() {
|
|
let stateSelector = name => ({ 'top . code postal': '3' }[name])
|
|
let data = {
|
|
taux_versement_transport: [
|
|
{ codePostal: 1, aot: { taux: { '2019': '1.0' } } },
|
|
{ codePostal: 2, smt: { taux: { '2019': '2.0' } } }
|
|
]
|
|
}
|
|
let rawRules = [
|
|
{ nom: 'top' },
|
|
{
|
|
espace: 'top',
|
|
nom: 'startHere',
|
|
formule: {
|
|
sélection: {
|
|
données: 'startHere',
|
|
cherche: 'code postal',
|
|
dans: 'codePostal',
|
|
renvoie: 'smt'
|
|
}
|
|
},
|
|
données: 'taux_versement_transport'
|
|
},
|
|
{ espace: 'top', nom: 'code postal', format: 'nombre' }
|
|
],
|
|
rules = parseAll(rawRules.map(rule => enrichRule(rule, data)))
|
|
expect(
|
|
analyse(rules, 'startHere')(stateSelector).targets[0]
|
|
).to.have.property('nodeValue', 0)
|
|
})
|
|
})
|
|
|
|
describe('Implicit parent applicability', function() {
|
|
it('should make a variable non applicable if one parent is input to false', function() {
|
|
let rawRules = [
|
|
{ nom: 'CDD', question: 'CDD ?' },
|
|
{ nom: 'surcoût', formule: 10, espace: 'CDD' }
|
|
],
|
|
rules = parseAll(rawRules.map(enrichRule))
|
|
expect(
|
|
analyse(rules, 'CDD . surcoût')(name => ({ CDD: false }[name])).targets[0]
|
|
).to.have.property('nodeValue', 0)
|
|
})
|
|
})
|