EduSync Learning API 데모 검증 보고서

2026-08-07T02:11:42Z에 Docker Compose, HTTP API, SQL Server로 검증했습니다. 자격 증명, HMAC 서명, 데이터베이스 비밀번호는 제외했습니다.

전체 결과: PASS

{"runtime":"Docker Compose / PHP 8.3.33","database":"PDO_SQLSRV를 통한 SQL Server","authentication":"로컬 테스트 자격 증명"}

시나리오 결과

시나리오결과HTTP / 테스트 상태요청응답세부 정보
상태 확인PASS200{"method":"GET","path":"/health"}{"status":"ok","database":"connected","driver":"pdo_sqlsrv","probe":1}
인증 필수PASS401{"method":"POST","path":"/api/v1/learning-events","authentication":"미포함"}{"error":{"status":401,"message":"Bearer authentication failed.","correlation_id":"c18fd77a6b6d0ab5fc6f6400e49f08ad"}}
유효한 수강 등록 필수PASS403{"method":"POST","path":"/api/v1/learning-events","authentication":"학습자 Bearer 값 제외"}{"error":{"status":403,"message":"Active enrollment is required.","correlation_id":"37ca2c8a8ed86b25ec2f8865f620202d"}}
신규 체크포인트PASS200{"method":"POST","path":"/api/v1/learning-events","body":{"event_id":"demo-checkpoint","learner_id":2001,"lecture_id":940000041,"session_id":"demo-session","sequence_no":1,"event_type":"CHECKPOINT","position_seconds":100,"occurred_at":"2026-08-07T02:09:42.433Z"}}{"applied":true,"duplicate":false}{"snapshot":{"resume_position_seconds":"100","furthest_position_seconds":"100","last_session_id":"demo-session","last_sequence_no":"1","completed_at":null}}
늦은 체크포인트는 이어보기를 유지하고 최대 위치를 높임PASS200{"method":"POST","path":"/api/v1/learning-events"}{"applied":true,"duplicate":false}{"snapshot":{"resume_position_seconds":"100","furthest_position_seconds":"150","last_session_id":"demo-session","last_sequence_no":"1","completed_at":null}}
최신 되감기는 이어보기를 낮추고 최대 위치를 유지함PASS200{"method":"POST","path":"/api/v1/learning-events"}{"applied":true,"duplicate":false}{"snapshot":{"resume_position_seconds":"50","furthest_position_seconds":"150","last_session_id":"demo-session","last_sequence_no":"2","completed_at":null}}
의미상 동일한 중복 요청PASS200{"method":"POST","path":"/api/v1/learning-events","body":"필드 순서 변경, 자격 증명 제외"}{"applied":false,"duplicate":true}
같은 이벤트 ID의 다른 payloadPASS409{"method":"POST","path":"/api/v1/learning-events"}{"error":{"status":409,"message":"event_id is already used with a different payload.","correlation_id":"052a0aeefba4e972ef8630ccf3eaa8f6"}}
서로 다른 source의 같은 이벤트 IDPASS200{"method":"POST","path":"/api/v1/player-events","authentication":"HMAC 헤더 값 제외"}{"applied":true,"duplicate":false}{"event_count":2}
5분을 초과한 미래 이벤트는 저장하지 않음PASS422{"method":"POST","path":"/api/v1/learning-events"}{"error":{"status":422,"message":"occurred_at is more than five minutes in the future.","correlation_id":"9d4a1aa7bad044da009cf53c4f92159c"}}{"event_count":0}
최초 completed_at 보존PASS200{"method":"POST","path":"/api/v1/learning-events"}{"applied":true,"duplicate":false}{"before":{"resume_position_seconds":"160","furthest_position_seconds":"160","last_session_id":"demo-complete","last_sequence_no":"1","completed_at":"2026-08-07 02:10:42.433"},"after":{"resume_position_seconds":"70","furthest_position_seconds":"160","last_session_id":"demo-complete","last_sequence_no":"2","completed_at":"2026-08-07 02:10:42.433"}}
보호자 경로 주체 불일치PASS403{"method":"GET","path":"/api/v1/guardians/{other}/learners/{seed}/progress","authentication":"보호자 Bearer 값 제외"}{"error":{"status":403,"message":"Authenticated guardian does not match guardianId.","correlation_id":"4b6cffe2e76c0068f7acdebfe2fcdf76"}}
보호자 연결 필수PASS403{"method":"GET","path":"/api/v1/guardians/{seed}/learners/{unlinked}/progress","authentication":"보호자 Bearer 값 제외"}{"error":{"status":403,"message":"Guardian is not linked to this learner.","correlation_id":"a0d7b25ea78ef75022810e72a4aa9986"}}
연결된 보호자의 진행 상태 조회PASS200{"method":"GET","path":"/api/v1/guardians/{seed}/learners/{seed}/progress","authentication":"보호자 Bearer 값 제외"}{"guardian_id":1001,"learner_id":2001,"progress":[{"lecture_id":4001,"resume_position_seconds":120,"furthest_position_seconds":180,"last_studied_at":"2026-07-01T10:00:00Z","completed_at":null},{"lecture_id":940000041,"resume_position_seconds":70,"furthest_position_seconds":160,"last_studied_at":"2026-08-07T02:10:52.433Z","completed_at":"2026-08-07T02:10:42.433Z"}]}
Classic ASP 계약PASS200{"command":"php tests/contract.php","target":"legacy/progress.asp"}"PASS Classic ASP contract and PDO_SQLSRV configuration"
동시 최초 INSERT와 MSSQL 원자성PASS200{"method":"병렬 HTTP 테스트","path":"tests/concurrency.php"}{"passed":true,"scenarios":["T01","T02","T03","T04","T08","T12"]}

최종 상태와 정리 결과

{ "event_count": 9, "snapshot": { "resume_position_seconds": "70", "furthest_position_seconds": "160", "last_session_id": "demo-complete", "last_sequence_no": "2", "completed_at": "2026-08-07 02:10:42.433" }, "fixture_cleaned": true }

검증 범위와 한계