@@ -12,6 +12,7 @@ const assert = require('node:assert/strict');
1212const { authorize, comment } = require ( './docs_preview.js' ) ;
1313
1414const REPO = { owner : 'modelcontextprotocol' , repo : 'python-sdk' } ;
15+ const BASE_REPO = { id : 1 , full_name : 'modelcontextprotocol/python-sdk' } ;
1516const HEAD = 'e4dfda7baa127ab00ebcd1d5324560cbe3cdfe42' ;
1617const MARKER = '<!-- docs-preview -->' ;
1718
@@ -37,6 +38,26 @@ const authorizeScenarios = [
3738 event : pushed ( 7 , 'writer' ) ,
3839 expect : { authorized : 'false' , pr_number : '7' , head_sha : HEAD , slash_attempt : 'false' } ,
3940 } ,
41+ {
42+ // actions/checkout refuses a fork's head under pull_request_target, so
43+ // the run stops here instead of failing in `build`; /preview-docs still works.
44+ name : 'admin pushes to or reopens a fork PR → no automatic preview' ,
45+ event : pushed ( 7 , 'admin' , { fork : 'someone/python-sdk' } ) ,
46+ expect : { authorized : 'false' , pr_number : '7' , head_sha : HEAD , slash_attempt : 'false' } ,
47+ permissionLookups : 0 ,
48+ } ,
49+ {
50+ name : 'fork PR whose fork has since been deleted → no automatic preview' ,
51+ event : pushed ( 7 , 'admin' , { fork : null } ) ,
52+ expect : { authorized : 'false' , pr_number : '7' , head_sha : HEAD , slash_attempt : 'false' } ,
53+ permissionLookups : 0 ,
54+ } ,
55+ {
56+ name : 'maintainer comments /preview-docs on a fork PR → previewed like any other' ,
57+ pr : { fork : 'someone/python-sdk' } ,
58+ event : slash ( 7 , 'maintainer' ) ,
59+ expect : { authorized : 'true' , pr_number : '7' , head_sha : HEAD , slash_attempt : 'true' } ,
60+ } ,
4061 {
4162 name : 'maintainer comments /preview-docs on an open PR → preview of its current head' ,
4263 event : slash ( 7 , 'maintainer' ) ,
@@ -70,6 +91,7 @@ for (const s of authorizeScenarios) {
7091 const world = makeWorld ( { pr : { number : 7 , ...s . pr } } ) ;
7192 assert . deepEqual ( await runAuthorize ( world , s . event ) , s . expect ) ;
7293 assert . equal ( world . writes . length , 0 ) ;
94+ if ( s . permissionLookups !== undefined ) assert . equal ( world . permissionLookups , s . permissionLookups ) ;
7395 } ) ;
7496}
7597
@@ -146,8 +168,10 @@ test('comment: a build or deploy that did not succeed is reported with the short
146168
147169// ── Harness ────────────────────────────────────────────────────────────────
148170
149- function pushed ( number , sender ) {
150- return { eventName : 'pull_request_target' , actor : sender , payload : { action : 'synchronize' , pull_request : { number, head : { sha : HEAD } } , sender : { login : sender } } } ;
171+ // `fork`: full name of the fork the head lives on; null for a deleted fork; omitted for a same-repo branch.
172+ function pushed ( number , sender , { fork } = { } ) {
173+ const repo = fork === undefined ? BASE_REPO : fork === null ? null : { id : 2 , full_name : fork } ;
174+ return { eventName : 'pull_request_target' , actor : sender , payload : { action : 'synchronize' , repository : BASE_REPO , pull_request : { number, head : { sha : HEAD , repo } } , sender : { login : sender } } } ;
151175}
152176function slash ( number , commenter ) {
153177 return { eventName : 'issue_comment' , actor : commenter , payload : { action : 'created' , issue : { number, pull_request : { } } , comment : { body : '/preview-docs' , user : { login : commenter } } } } ;
@@ -173,7 +197,7 @@ async function runComment(world, env, actor) {
173197// ── A tiny in-memory GitHub ────────────────────────────────────────────────
174198
175199function makeWorld ( { pr, comments = [ ] } ) {
176- const world = { pr : { state : 'open' , ...pr } , comments : [ ] , writes : [ ] , failPermissionLookup : false , nextCommentId : 100 } ;
200+ const world = { pr : { state : 'open' , ...pr } , comments : [ ] , writes : [ ] , failPermissionLookup : false , permissionLookups : 0 , nextCommentId : 100 } ;
177201 for ( const c of comments ) world . comments . push ( { id : world . nextCommentId ++ , ...c } ) ;
178202
179203 const err = ( status , message = 'fake error' ) => Object . assign ( new Error ( message ) , { status } ) ;
@@ -183,14 +207,19 @@ function makeWorld({ pr, comments = [] }) {
183207 const rest = {
184208 repos : {
185209 getCollaboratorPermissionLevel : async ( { username } ) => {
210+ world . permissionLookups ++ ;
186211 if ( world . failPermissionLookup ) throw err ( 500 , 'boom' ) ;
187212 const person = PEOPLE [ username ] ;
188213 if ( ! person ) throw err ( 404 , 'not a user' ) ;
189214 return { data : { ...person , user : { login : username } } } ;
190215 } ,
191216 } ,
192217 pulls : {
193- get : async ( { pull_number } ) => { checkPr ( pull_number ) ; return { data : { number : pull_number , state : world . pr . state , head : { sha : HEAD } } } ; } ,
218+ get : async ( { pull_number } ) => {
219+ checkPr ( pull_number ) ;
220+ const repo = world . pr . fork ? { id : 2 , full_name : world . pr . fork } : BASE_REPO ;
221+ return { data : { number : pull_number , state : world . pr . state , head : { sha : HEAD , repo } } } ;
222+ } ,
194223 } ,
195224 issues : {
196225 listComments : async ( { issue_number } ) => { checkPr ( issue_number ) ; return { data : world . comments . map ( ( c ) => ( { id : c . id , body : c . body , user : { login : c . user } } ) ) } ; } ,
0 commit comments