This commit is contained in:
Doar Mihai
2026-10-17 02:01:43 +03:00
parent 402fd63721
commit 579d487460
5 changed files with 226 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
<?php
use Laya\Result\DecisionSet;
function sampleResponse(): array
{
return [
'routing' => ['model' => 'm1'],
'answers' => [
'category' => ['type' => 'choice', 'choice' => 'billing', 'confidence' => 0.9, 'distribution' => ['billing' => 0.9, 'bug' => 0.1]],
'urgency' => ['type' => 'score', 'score' => '2', 'confidence' => 0.5],
'angry' => ['type' => 'noul', 'noul' => 0.93],
],
];
}
it('parses each answer into a decision', function () {
$set = DecisionSet::fromResponse(sampleResponse());
expect($set->routedModel)->toBe('m1')
->and($set->get('category')->choice())->toBe('billing')
->and($set->get('category')->type)->toBe('choice')
->and($set->get('category')->confidence())->toBe(0.9)
->and($set->get('category')->distribution())->toBe(['billing' => 0.9, 'bug' => 0.1])
->and($set->get('urgency')->score())->toBe(2.0)
->and($set->get('angry')->probability())->toBe(0.93);
});
it('returns null for fields a decision does not have', function () {
$d = DecisionSet::fromResponse(sampleResponse())->get('angry');
expect($d->choice())->toBeNull()
->and($d->score())->toBeNull()
->and($d->confidence())->toBeNull();
});
it('handles a response with no answers or routing', function () {
$set = DecisionSet::fromResponse([]);
expect($set->routedModel)->toBeNull();
$set->get('x');
})->throws(OutOfBoundsException::class, "No answer for question 'x'");
it('throws for an unknown question name', function () {
DecisionSet::fromResponse(sampleResponse())->get('missing');
})->throws(OutOfBoundsException::class);
it('uses unknown as the type when the server omits it', function () {
$set = DecisionSet::fromResponse(['answers' => ['q' => ['choice' => 'a']]]);
expect($set->get('q')->type)->toBe('unknown');
});
it('returns only the distribution field, not the whole payload', function () {
$d = DecisionSet::fromResponse(['answers' => ['q' => ['type' => 'choice', 'choice' => 'a']]])->get('q');
expect($d->distribution())->toBe([]);
})->todo('CODE_REVIEW #8: distribution() currently falls back to the whole answer');
+55
View File
@@ -0,0 +1,55 @@
<?php
use Laya\Question\ChoiceQuestion;
use Laya\Question\QuestionSet;
use Laya\Question\ScoreQuestion;
use Laya\Question\YesNoQuestion;
it('serialises a choice question', function () {
$q = (new ChoiceQuestion('category', 'Which?'))->option('a', 'First')->option('b', 'Second');
expect($q->name())->toBe('category')
->and($q->toArray())->toBe([
'type' => 'choice',
'instructions' => 'Which?',
'criteria' => ['a' => 'First', 'b' => 'Second'],
]);
});
it('serialises a score question', function () {
$q = new ScoreQuestion('urgency', 'How urgent?', ['low', 'high']);
expect($q->toArray())->toBe([
'type' => 'score',
'instructions' => 'How urgent?',
'criteria' => ['low', 'high'],
]);
});
it('serialises a yes/no question using the noul wire type', function () {
expect((new YesNoQuestion('angry', 'Upset?'))->toArray())
->toBe(['type' => 'noul', 'instructions' => 'Upset?']);
});
it('keys a question set by question name', function () {
$set = QuestionSet::make()
->add(new YesNoQuestion('a', 'A?'))
->add(new YesNoQuestion('b', 'B?'));
expect(array_keys($set->toArray()))->toBe(['a', 'b']);
});
it('replaces an earlier question with the same name', function () {
$set = QuestionSet::make()
->add(new YesNoQuestion('a', 'first'))
->add(new YesNoQuestion('a', 'second'));
expect($set->toArray())->toHaveCount(1)
->and($set->toArray()['a']['instructions'])->toBe('second');
});
it('encodes a choice question without options as a JSON object', function () {
$json = json_encode((new ChoiceQuestion('c', 'x'))->toArray());
expect($json)->toContain('"criteria":{}');
})->todo('CODE_REVIEW #7: empty criteria currently encodes as []');