@@ -87,3 +87,132 @@ func TestGetIntegration_NotConnected(t *testing.T) {
8787 t .Fatalf ("expected project_id=%s, got %s" , testProjectID , env .Data .ProjectID )
8888 }
8989}
90+
91+ // ── PR review/comment tests ────────────────────────────────────────────────────
92+ //
93+ // These cover the paths reachable without an outbound GitHub API call
94+ // (validation and "PR not linked to this task"). The actual GitHub call
95+ // (ghClient -> plugin.Fetch) always errors outside a WASM build — see
96+ // plugin-sdk-go's native_backends.go — so the happy path for these three
97+ // handlers, like the existing createPullRequest handler, isn't unit-testable
98+ // here and is covered manually / by integration testing instead.
99+
100+ func reqWithPathParams (params map [string ]string ) plugintest.Request {
101+ r := callerReq ()
102+ for k , v := range params {
103+ r .PathParams [k ] = v
104+ }
105+ return r
106+ }
107+
108+ func TestGetPullRequestDetails_NotLinkedToTask (t * testing.T ) {
109+ tc := setupPlugin (t )
110+ req := reqWithPathParams (map [string ]string {"taskId" : testTaskID , "prId" : "pr-1" })
111+ res := tc .Call ("GET" , "/tasks/:taskId/pull-requests/:prId" , req )
112+
113+ if res .StatusCode != 404 {
114+ t .Fatalf ("expected 404, got %d: %s" , res .StatusCode , res .BodyString ())
115+ }
116+ }
117+
118+ func TestAddPullRequestComment_MissingBody (t * testing.T ) {
119+ tc := setupPlugin (t )
120+ req := reqWithPathParams (map [string ]string {"taskId" : testTaskID , "prId" : "pr-1" }).
121+ WithJSONBody (map [string ]string {})
122+ res := tc .Call ("POST" , "/tasks/:taskId/pull-requests/:prId/comments" , req )
123+
124+ if res .StatusCode != 400 {
125+ t .Fatalf ("expected 400, got %d: %s" , res .StatusCode , res .BodyString ())
126+ }
127+ }
128+
129+ func TestAddPullRequestComment_NotLinkedToTask (t * testing.T ) {
130+ tc := setupPlugin (t )
131+ req := reqWithPathParams (map [string ]string {"taskId" : testTaskID , "prId" : "pr-1" }).
132+ WithJSONBody (map [string ]string {"body" : "looks good" })
133+ res := tc .Call ("POST" , "/tasks/:taskId/pull-requests/:prId/comments" , req )
134+
135+ if res .StatusCode != 404 {
136+ t .Fatalf ("expected 404, got %d: %s" , res .StatusCode , res .BodyString ())
137+ }
138+ }
139+
140+ func TestCreateReview_InvalidEvent (t * testing.T ) {
141+ tc := setupPlugin (t )
142+ req := reqWithPathParams (map [string ]string {"taskId" : testTaskID , "prId" : "pr-1" }).
143+ WithJSONBody (map [string ]string {"event" : "NOT_A_REAL_EVENT" })
144+ res := tc .Call ("POST" , "/tasks/:taskId/pull-requests/:prId/reviews" , req )
145+
146+ if res .StatusCode != 400 {
147+ t .Fatalf ("expected 400, got %d: %s" , res .StatusCode , res .BodyString ())
148+ }
149+ }
150+
151+ func TestCreateReview_NotLinkedToTask (t * testing.T ) {
152+ tc := setupPlugin (t )
153+ req := reqWithPathParams (map [string ]string {"taskId" : testTaskID , "prId" : "pr-1" }).
154+ WithJSONBody (map [string ]string {"event" : "APPROVE" })
155+ res := tc .Call ("POST" , "/tasks/:taskId/pull-requests/:prId/reviews" , req )
156+
157+ if res .StatusCode != 404 {
158+ t .Fatalf ("expected 404, got %d: %s" , res .StatusCode , res .BodyString ())
159+ }
160+ }
161+
162+ func TestGetPullRequestCIStatus_NotLinkedToTask (t * testing.T ) {
163+ tc := setupPlugin (t )
164+ req := reqWithPathParams (map [string ]string {"taskId" : testTaskID , "prId" : "pr-1" })
165+ res := tc .Call ("GET" , "/tasks/:taskId/pull-requests/:prId/ci-status" , req )
166+
167+ if res .StatusCode != 404 {
168+ t .Fatalf ("expected 404, got %d: %s" , res .StatusCode , res .BodyString ())
169+ }
170+ }
171+
172+ // ── overallCIState ─────────────────────────────────────────────────────────────
173+
174+ func TestOverallCIState_NoChecks (t * testing.T ) {
175+ if got := overallCIState (nil ); got != "unknown" {
176+ t .Fatalf ("expected unknown, got %s" , got )
177+ }
178+ }
179+
180+ func TestOverallCIState_AllSuccess (t * testing.T ) {
181+ checks := []ciCheckResponse {
182+ {Status : "completed" , Conclusion : "success" },
183+ {Status : "completed" , Conclusion : "neutral" },
184+ }
185+ if got := overallCIState (checks ); got != "success" {
186+ t .Fatalf ("expected success, got %s" , got )
187+ }
188+ }
189+
190+ func TestOverallCIState_OneFailureWins (t * testing.T ) {
191+ checks := []ciCheckResponse {
192+ {Status : "completed" , Conclusion : "success" },
193+ {Status : "completed" , Conclusion : "failure" },
194+ }
195+ if got := overallCIState (checks ); got != "failure" {
196+ t .Fatalf ("expected failure, got %s" , got )
197+ }
198+ }
199+
200+ func TestOverallCIState_StillRunningIsPending (t * testing.T ) {
201+ checks := []ciCheckResponse {
202+ {Status : "completed" , Conclusion : "success" },
203+ {Status : "in_progress" , Conclusion : "" },
204+ }
205+ if got := overallCIState (checks ); got != "pending" {
206+ t .Fatalf ("expected pending, got %s" , got )
207+ }
208+ }
209+
210+ func TestOverallCIState_FailureBeatsPending (t * testing.T ) {
211+ checks := []ciCheckResponse {
212+ {Status : "in_progress" , Conclusion : "" },
213+ {Status : "completed" , Conclusion : "failure" },
214+ }
215+ if got := overallCIState (checks ); got != "failure" {
216+ t .Fatalf ("expected failure, got %s" , got )
217+ }
218+ }
0 commit comments