service = new KnowledgeGraphService(); } /** @test */ public function it_initializes_with_correct_base_url() { // 默认URL $this->assertEquals('http://localhost:5011', $this->service->baseUrl); // 测试自定义URL $this->service = new KnowledgeGraphService(); $this->service->baseUrl = 'http://knowledge-api.test'; $this->assertEquals('http://knowledge-api.test', $this->service->baseUrl); } /** @test */ public function it_trims_trailing_slash_from_base_url() { $this->service->baseUrl = 'http://localhost:5011/'; $this->assertEquals('http://localhost:5011', $this->service->baseUrl); } /** @test */ public function list_knowledge_points_returns_formatted_data() { Http::fake([ 'localhost:5011/knowledge-points/*' => Http::response([ 'data' => [ [ 'id' => 'kp_1', 'kp_code' => 'KP1001', 'cn_name' => '因式分解基础', 'category' => '数学', 'phase' => '初中', 'grade' => 8, 'importance' => 5, ], ], ], 200), ]); $result = $this->service->listKnowledgePoints(); $this->assertIsArray($result); $this->assertCount(1, $result); $this->assertEquals('kp_1', $result[0]['id']); $this->assertEquals('KP1001', $result[0]['kp_code']); $this->assertEquals('因式分解基础', $result[0]['cn_name']); $this->assertEquals('数学', $result[0]['category']); $this->assertEquals('初中', $result[0]['phase']); } /** @test */ public function list_knowledge_points_returns_fallback_data_on_error() { Http::fake([ 'localhost:5011/knowledge-points/*' => Http::response([], 500), ]); $result = $this->service->listKnowledgePoints(); // 应该返回备用数据 $this->assertIsArray($result); $this->assertGreaterThan(0, count($result)); $this->assertArrayHasKey('id', $result[0]); $this->assertArrayHasKey('kp_code', $result[0]); $this->assertArrayHasKey('cn_name', $result[0]); } /** @test */ public function list_knowledge_points_handles_multiple_field_names() { Http::fake([ 'localhost:5011/knowledge-points/*' => Http::response([ 'data' => [ [ 'kp_id' => 'kp_1', // 使用kp_id而不是id 'kp_name' => '因式分解', // 使用kp_name而不是cn_name ], ], ], 200), ]); $result = $this->service->listKnowledgePoints(); $this->assertEquals('kp_1', $result[0]['id']); $this->assertEquals('KP_UNKNOWN', $result[0]['kp_code']); $this->assertEquals('因式分解', $result[0]['cn_name']); } /** @test */ public function get_skills_by_knowledge_point_returns_formatted_data() { Http::fake([ 'localhost:5011/graph/node/*' => Http::response([ 'skills' => [ [ 'skill_code' => 'SK001', 'skill_name' => '计算能力', 'weight' => 0.9, ], ], ], 200), ]); $result = $this->service->getSkillsByKnowledgePoint('KP1001'); $this->assertIsArray($result); $this->assertCount(1, $result); $this->assertEquals('SK001', $result[0]['code']); $this->assertEquals('计算能力', $result[0]['name']); $this->assertEquals(0.9, $result[0]['weight']); } /** @test */ public function get_skills_by_knowledge_point_returns_empty_on_error() { Http::fake([ 'localhost:5011/graph/node/*' => Http::response([], 500), ]); $result = $this->service->getSkillsByKnowledgePoint('KP1001'); $this->assertIsArray($result); $this->assertEmpty($result); } /** @test */ public function list_skills_returns_formatted_data() { Http::fake([ 'localhost:5011/skills/*' => Http::response([ 'data' => [ [ 'id' => 'sk_1', 'skill_code' => 'SK001', 'skill_name' => '计算能力', 'skill_type' => '基础技能', ], ], ], 200), ]); $result = $this->service->listSkills(); $this->assertIsArray($result); $this->assertCount(1, $result); $this->assertEquals('sk_1', $result[0]['id']); $this->assertEquals('SK001', $result[0]['code']); $this->assertEquals('计算能力', $result[0]['name']); $this->assertEquals('基础技能', $result[0]['category']); } /** @test */ public function list_relations_returns_formatted_data() { Http::fake([ 'localhost:5011/graph/relations' => Http::response([ 'data' => [ [ 'source' => 'R01', 'target' => 'R02', 'relation_type' => 'PREREQUISITE', 'relation_direction' => 'DOWNSTREAM', 'weight' => 0.9, 'description' => '前置依赖', ], ], ], 200), ]); $result = $this->service->listRelations(); $this->assertIsArray($result); $this->assertCount(1, $result); $this->assertEquals('R01', $result[0]['source_kp']); $this->assertEquals('R02', $result[0]['target_kp']); $this->assertEquals('PREREQUISITE', $result[0]['relation_type']); $this->assertEquals('DOWNSTREAM', $result[0]['relation_direction']); $this->assertEquals(0.9, $result[0]['weight']); } /** @test */ public function export_graph_returns_api_data() { $graphData = [ 'nodes' => [ ['id' => 'R01', 'label' => '有理数'], ], 'edges' => [ ['source' => 'R01', 'target' => 'R02'], ], ]; Http::fake([ 'localhost:5011/graph/export' => Http::response($graphData, 200), ]); $result = $this->service->exportGraph(); $this->assertIsArray($result); $this->assertArrayHasKey('nodes', $result); $this->assertArrayHasKey('edges', $result); $this->assertCount(1, $result['nodes']); $this->assertCount(1, $result['edges']); } /** @test */ public function export_graph_returns_empty_on_error() { Http::fake([ 'localhost:5011/graph/export' => Http::response([], 500), ]); $result = $this->service->exportGraph(); $this->assertIsArray($result); $this->assertArrayHasKey('nodes', $result); $this->assertArrayHasKey('edges', $result); $this->assertEmpty($result['nodes']); $this->assertEmpty($result['edges']); } /** @test */ public function check_health_returns_true_when_healthy() { Http::fake([ 'localhost:5011/health' => Http::response([], 200), ]); $result = $this->service->checkHealth(); $this->assertTrue($result); } /** @test */ public function check_health_returns_false_when_unhealthy() { Http::fake([ 'localhost:5011/health' => Http::response([], 500), ]); $result = $this->service->checkHealth(); $this->assertFalse($result); } /** @test */ public function check_health_returns_false_on_exception() { Http::fake([ 'localhost:5011/health' => Http::throw(new \Exception('Connection failed')), ]); $result = $this->service->checkHealth(); $this->assertFalse($result); } /** @test */ public function get_mastery_color_returns_correct_colors() { $this->assertEquals('#10b981', $this->service->getMasteryColor(0.9)); $this->assertEquals('#3b82f6', $this->service->getMasteryColor(0.7)); $this->assertEquals('#f59e0b', $this->service->getMasteryColor(0.5)); $this->assertEquals('#f97316', $this->service->getMasteryColor(0.3)); $this->assertEquals('#ef4444', $this->service->getMasteryColor(0.1)); } /** @test */ public function get_mastery_color_handles_edge_cases() { $this->assertEquals('#10b981', $this->service->getMasteryColor(1.0)); $this->assertEquals('#ef4444', $this->service->getMasteryColor(0.0)); $this->assertEquals('#ef4444', $this->service->getMasteryColor(-0.1)); } /** @test */ public function get_student_mastery_returns_api_data() { Http::fake([ 'localhost:5010/api/mastery/*' => Http::response([ 'masteries' => [ ['kp_code' => 'R01', 'mastery_level' => 0.85, 'confidence_level' => 0.8], ], ], 200), ]); $result = $this->service->getStudentMastery(1001); $this->assertIsArray($result); $this->assertArrayHasKey('masteries', $result); $this->assertCount(1, $result['masteries']); } /** @test */ public function get_student_mastery_returns_mock_data_on_error() { Http::fake([ 'localhost:5010/api/mastery/*' => Http::response([], 500), ]); $result = $this->service->getStudentMastery(1001); $this->assertIsArray($result); $this->assertArrayHasKey('masteries', $result); $this->assertCount(5, $result['masteries']); } /** @test */ public function get_student_statistics_returns_api_data() { Http::fake([ 'localhost:5010/api/mastery/*/statistics' => Http::response([ 'total_knowledge_points' => 8, 'average_mastery' => 0.65, 'high_mastery_count' => 2, 'medium_mastery_count' => 3, 'low_mastery_count' => 3, ], 200), ]); $result = $this->service->getStudentStatistics(1001); $this->assertIsArray($result); $this->assertEquals(8, $result['total_knowledge_points']); $this->assertEquals(0.65, $result['average_mastery']); $this->assertEquals(2, $result['high_mastery_count']); } /** @test */ public function get_student_statistics_returns_mock_data_on_error() { Http::fake([ 'localhost:5010/api/mastery/*/statistics' => Http::response([], 500), ]); $result = $this->service->getStudentStatistics(1001); $this->assertIsArray($result); $this->assertEquals(5, $result['total_knowledge_points']); $this->assertEquals(0.594, $result['average_mastery']); } /** @test */ public function get_student_statistics_handles_api_exception() { Http::fake([ 'localhost:5010/api/mastery/*/statistics' => Http::throw(new \Exception('API Error')), ]); $result = $this->service->getStudentStatistics(1001); $this->assertIsArray($result); $this->assertArrayHasKey('total_knowledge_points', $result); $this->assertArrayHasKey('average_mastery', $result); } /** @test */ public function import_graph_calls_api_correctly() { Http::fake([ 'localhost:5011/import/graph' => Http::response([ 'success' => true, 'imported' => 100, ], 200), ]); $treeData = ['nodes' => []]; $edgesData = ['edges' => []]; $result = $this->service->importGraph($treeData, $edgesData); $this->assertTrue($result); } /** @test */ public function import_graph_handles_api_error() { Http::fake([ 'localhost:5011/import/graph' => Http::response([ 'success' => false, 'error' => 'Invalid data', ], 400), ]); $treeData = ['nodes' => []]; $edgesData = ['edges' => []]; $result = $this->service->importGraph($treeData, $edgesData); $this->assertFalse($result); } /** @test */ public function get_knowledge_point_calls_correct_endpoint() { Http::fake([ 'localhost:5011/knowledge-points/*' => Http::response([ 'id' => 'KP1001', 'name' => '因式分解基础', ], 200), ]); $result = $this->service->getKnowledgePoint('KP1001'); $this->assertIsArray($result); $this->assertEquals('KP1001', $result['id']); } /** @test */ public function create_knowledge_point_calls_correct_endpoint() { Http::fake([ 'localhost:5011/knowledge-points/*' => Http::response([], 201), ]); $data = [ 'kp_code' => 'KP1001', 'cn_name' => '因式分解基础', 'category' => '数学', ]; $result = $this->service->createKnowledgePoint($data); $this->assertTrue($result); } /** @test */ public function update_knowledge_point_calls_correct_endpoint() { Http::fake([ 'localhost:5011/knowledge-points/*' => Http::response([], 200), ]); $data = [ 'cn_name' => '因式分解进阶', ]; $result = $this->service->updateKnowledgePoint('KP1001', $data); $this->assertTrue($result); } /** @test */ public function delete_knowledge_point_calls_correct_endpoint() { Http::fake([ 'localhost:5011/knowledge-points/*' => Http::response([], 204), ]); $result = $this->service->deleteKnowledgePoint('KP1001'); $this->assertTrue($result); } }