mirror of
https://github.com/betagouv/mon-entreprise
synced 2025-02-08 22:15:02 +00:00
Plus simple que be7b2b8ac4e747b6a7cb4d56e2edc8544875f4c4 This feature was at first designed to work not only with boolean questions, but also applicability conditions and formulas. But lots of numeric formulas are also namespaces, with children in the computations themselves. Which, in the previous implementations, lead to lots of irrelevant checks. To be reintroduced better if needed
520 lines
13 KiB
JavaScript
520 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 des tranches': 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 des tranches': 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 des tranches': 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 des tranches': 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 des tranches': 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)
|
|
})
|
|
})
|