You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
8.3 KiB
167 lines
8.3 KiB
<?php
|
|
|
|
namespace Tests\Unit\Survey;
|
|
|
|
use App\Services\Survey\SurveySchema;
|
|
use App\Services\Survey\SurveyStatistics;
|
|
use InvalidArgumentException;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class SurveySchemaTest extends TestCase
|
|
{
|
|
private function questions(): array
|
|
{
|
|
return [
|
|
['id' => 1, 'field' => 'section', 'name' => '资源对接', 'edit_input' => 'section'],
|
|
['id' => 2, 'field' => 'resource', 'name' => '融资', 'edit_input' => 'multi_dimension', 'need_fill' => true, 'dimensions' => [
|
|
['field' => 'intent', 'name' => '意向', 'need_fill' => true, 'select_item' => [['key' => 'yes', 'value' => '是'], ['key' => 'no', 'value' => '否']]],
|
|
['field' => 'quantity', 'name' => '数量', 'edit_input' => 'number', 'need_fill' => true, 'config' => ['min' => 0, 'max' => 100, 'integer' => true]],
|
|
]],
|
|
['id' => 3, 'field' => 'rating', 'name' => '满意度', 'edit_input' => 'radio', 'config' => ['is_score' => true], 'select_item' => [['key' => 'a', 'value' => '满意', 'score' => 5], ['key' => 'b', 'value' => '一般', 'score' => 3]]],
|
|
['id' => 4, 'field' => 'needs', 'name' => '需求', 'edit_input' => 'checkbox', 'config' => ['other_key' => 'other', 'other_required' => true], 'select_item' => [['key' => 'bank', 'value' => '银行'], ['key' => 'other', 'value' => '其他']]],
|
|
];
|
|
}
|
|
|
|
public function testServerSchemaAndZeroArePreserved(): void
|
|
{
|
|
$q = $this->questions();
|
|
$answers = SurveySchema::normalize($q, [
|
|
['id' => 2, 'name' => '篡改标题', 'value' => ['intent' => 'no', 'quantity' => '0', 'unknown' => 8]],
|
|
['id' => 3, 'value' => 'a', 'select_item' => [['key' => 'a', 'score' => 999]]],
|
|
], true);
|
|
$this->assertSame('融资', $answers[1]['name']);
|
|
$this->assertSame(['intent' => 'no', 'quantity' => 0.0], $answers[1]['value']);
|
|
$this->assertSame(5, $answers[2]['select_item'][0]['score']);
|
|
$this->assertSame([], $answers[3]['value']);
|
|
$reply = SurveySchema::replyQuestions([], $answers, ['version' => 2, 'questions' => $q]);
|
|
$this->assertSame('0', $reply[1]['dimensions'][1]['display_value']);
|
|
$this->assertSame('否', $reply[1]['dimensions'][0]['display_value']);
|
|
}
|
|
|
|
public function testReplySelectionsSupportSnapshotsLegacyAndZeroKeys(): void
|
|
{
|
|
$q = $this->questions();
|
|
$q[2]['select_item'][] = ['key' => 0, 'value' => '零分'];
|
|
$reply = SurveySchema::replyQuestions([], [
|
|
['id' => 2, 'value' => ['intent' => 'no']],
|
|
['id' => 3, 'value' => 0],
|
|
['id' => 4, 'value' => ['bank', 'other']],
|
|
], ['version' => 2, 'questions' => $q]);
|
|
$this->assertSame(['no'], $reply[1]['dimensions'][0]['selected_keys']);
|
|
$this->assertSame([], $reply[1]['dimensions'][1]['selected_keys']);
|
|
$this->assertSame(['0'], $reply[2]['selected_keys']);
|
|
$this->assertSame(['bank', 'other'], $reply[3]['selected_keys']);
|
|
$legacy = SurveySchema::replyQuestions($q, [
|
|
['id' => 2, 'value' => ['intent' => '否']],
|
|
['id' => 3, 'value' => '满意'],
|
|
['id' => 4, 'value' => '银行,其他'],
|
|
], null);
|
|
$this->assertSame(['no'], $legacy[1]['dimensions'][0]['selected_keys']);
|
|
$this->assertSame(['a'], $legacy[2]['selected_keys']);
|
|
$this->assertSame(['bank', 'other'], $legacy[3]['selected_keys']);
|
|
$empty = SurveySchema::replyQuestions($q, [], null);
|
|
$this->assertSame([], $empty[2]['selected_keys']);
|
|
}
|
|
|
|
public function testCustomScoresAreNotLimitedToFivePointScale(): void
|
|
{
|
|
$q = $this->questions()[2];
|
|
$q['select_item'] = [['key' => 'custom', 'value' => '自定义', 'score' => -2.5], ['key' => 'ten', 'value' => '10分', 'score' => 10]];
|
|
$q = SurveySchema::question($q);
|
|
$this->assertSame(-2.5, $q['select_item'][0]['score']);
|
|
$this->assertSame(10.0, $q['select_item'][1]['score']);
|
|
}
|
|
|
|
public function testCombinationRequiredIsControlledByParent(): void
|
|
{
|
|
$q = $this->questions()[1];
|
|
$q['need_fill'] = false;
|
|
$q['rule'] = '';
|
|
$normalized = SurveySchema::normalize([$q], [], true);
|
|
$this->assertNull($normalized[0]['value']['intent']);
|
|
$q['need_fill'] = true;
|
|
$this->expectException(InvalidArgumentException::class);
|
|
SurveySchema::normalize([$q], [['id' => 2, 'value' => ['intent' => 'yes']]], true);
|
|
}
|
|
|
|
/** @dataProvider invalidNumberProvider */
|
|
public function testInvalidNumbersAreRejected($value): void
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
SurveySchema::normalize($this->questions(), [['id' => 2, 'value' => ['intent' => 'yes', 'quantity' => $value]]], true);
|
|
}
|
|
|
|
public function invalidNumberProvider(): array { return [[-1], [1.5], [101], ['abc'], [null], ['1e999']]; }
|
|
|
|
public function testOtherRequiresTextAndUnselectedTextIsDiscarded(): void
|
|
{
|
|
$q = [$this->questions()[3]];
|
|
$answer = SurveySchema::normalize($q, [['id' => 4, 'value' => ['bank'], 'other_text' => '旧内容']], true);
|
|
$this->assertSame('', $answer[0]['other_text']);
|
|
$this->expectException(InvalidArgumentException::class);
|
|
SurveySchema::normalize($q, [['id' => 4, 'value' => ['other']]], true);
|
|
}
|
|
|
|
public function testForgedOptionsCannotBeSubmitted(): void
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
SurveySchema::normalize([$this->questions()[2]], [['id' => 3, 'value' => 'fake']], true);
|
|
}
|
|
|
|
public function testLegacyTextOptionsAndSerializedDimensions(): void
|
|
{
|
|
$q = $this->questions();
|
|
$answers = [['id' => 2, 'value' => json_encode(['intent' => '否', 'quantity' => 0])], ['id' => 3, 'value' => '满意']];
|
|
$normalized = SurveySchema::normalize($q, $answers);
|
|
$this->assertSame('no', $normalized[1]['value']['intent']);
|
|
$this->assertSame('a', $normalized[2]['value']);
|
|
$reply = SurveySchema::replyQuestions($q, $answers, null);
|
|
$this->assertSame('否', $reply[1]['dimensions'][0]['display_value']);
|
|
$this->assertSame('0', $reply[1]['dimensions'][1]['display_value']);
|
|
}
|
|
|
|
public function testStatisticsUseValidAnswersAndCountSelectionsOnce(): void
|
|
{
|
|
$q = $this->questions();
|
|
$stats = SurveyStatistics::summarize($q, [
|
|
['snapshot' => ['version' => 2], 'data' => [['id' => 2, 'value' => ['intent' => 'no', 'quantity' => 0]], ['id' => 3, 'value' => 'a'], ['id' => 4, 'value' => ['bank', 'other', 'bank'], 'other_text' => '融资']]],
|
|
['data' => [['id' => 2, 'value' => '{"intent":"是","quantity":3}'], ['id' => 3, 'value' => '一般']]],
|
|
['data' => []],
|
|
]);
|
|
$this->assertSame(3, $stats['responses']);
|
|
$this->assertSame(3, $stats['questions_count']);
|
|
$this->assertEquals(4, $stats['average']);
|
|
$this->assertSame(2, $stats['questions'][1]['dimensions'][1]['stats']['valid_count']);
|
|
$this->assertEquals(3, $stats['questions'][1]['dimensions'][1]['stats']['sum']);
|
|
$this->assertEquals(100, $stats['questions'][3]['stats']['options'][0]['percentage']);
|
|
$this->assertSame(1, $stats['questions'][3]['stats']['options'][0]['count']);
|
|
$this->assertSame(1, $stats['questions'][3]['stats']['other_count']);
|
|
}
|
|
|
|
public function testSnapshotSurvivesChangedCurrentDefinitions(): void
|
|
{
|
|
$original = $this->questions()[2];
|
|
$current = array_replace($original, ['name' => '新问题']);
|
|
$reply = SurveySchema::replyQuestions([$current], [['id' => 3, 'value' => 'a']], ['version' => 2, 'questions' => [$original]]);
|
|
$this->assertSame('满意度', $reply[0]['name']);
|
|
$this->assertSame('满意', $reply[0]['display_value']);
|
|
}
|
|
|
|
public function testQuestionValidationRejectsDuplicateDimensionIdentifiers(): void
|
|
{
|
|
$q = $this->questions()[1];
|
|
$q['dimensions'][1]['field'] = 'intent';
|
|
$this->expectException(InvalidArgumentException::class);
|
|
SurveySchema::question($q);
|
|
}
|
|
|
|
public function testDeletedLegacyQuestionRemainsVisible(): void
|
|
{
|
|
$q = $this->questions()[2];
|
|
$q['value'] = '满意';
|
|
$reply = SurveySchema::replyQuestions([], [$q], null);
|
|
$this->assertCount(1, $reply);
|
|
$this->assertSame('满意', $reply[0]['display_value']);
|
|
}
|
|
}
|