@@ -112,9 +112,148 @@ func TestDoltHubProvider_ForkDispatch_WithSessionToken(t *testing.T) {
112112 }
113113}
114114
115+ func TestDoltHubProvider_ForkREST_Success (t * testing.T ) {
116+ // REST fork: POST returns operation_name, poll returns success.
117+ pollCount := 0
118+ apiServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
119+ if r .Header .Get ("authorization" ) != "token api-token" {
120+ t .Errorf ("expected auth header, got %q" , r .Header .Get ("authorization" ))
121+ }
122+ if r .Method == "POST" && strings .HasSuffix (r .URL .Path , "/fork" ) {
123+ var body map [string ]string
124+ if err := json .NewDecoder (r .Body ).Decode (& body ); err != nil {
125+ t .Errorf ("decoding request: %v" , err )
126+ }
127+ if body ["ownerName" ] != "alice-dev" {
128+ t .Errorf ("ownerName = %q, want %q" , body ["ownerName" ], "alice-dev" )
129+ }
130+ if body ["parentOwnerName" ] != "steveyegge" {
131+ t .Errorf ("parentOwnerName = %q, want %q" , body ["parentOwnerName" ], "steveyegge" )
132+ }
133+ if body ["parentDatabaseName" ] != "wl-commons" {
134+ t .Errorf ("parentDatabaseName = %q, want %q" , body ["parentDatabaseName" ], "wl-commons" )
135+ }
136+ w .WriteHeader (200 )
137+ _ , _ = w .Write ([]byte (`{"status":"Success","operation_name":"fork-op-123"}` ))
138+ return
139+ }
140+ if r .Method == "GET" && r .URL .Query ().Get ("operationName" ) == "fork-op-123" {
141+ pollCount ++
142+ if pollCount < 2 {
143+ w .WriteHeader (200 )
144+ _ , _ = w .Write ([]byte (`{"status":"Pending"}` ))
145+ return
146+ }
147+ w .WriteHeader (200 )
148+ _ , _ = w .Write ([]byte (`{"owner_name":"alice-dev","database_name":"wl-commons"}` ))
149+ return
150+ }
151+ w .WriteHeader (404 )
152+ }))
153+ defer apiServer .Close ()
154+
155+ oldAPI := dolthubAPIBase
156+ dolthubAPIBase = apiServer .URL
157+ defer func () { dolthubAPIBase = oldAPI }()
158+
159+ provider := NewDoltHubProvider ("api-token" )
160+ err := provider .forkREST ("steveyegge" , "wl-commons" , "alice-dev" )
161+ if err != nil {
162+ t .Errorf ("forkREST should succeed: %v" , err )
163+ }
164+ }
165+
166+ func TestDoltHubProvider_ForkREST_AlreadyExists (t * testing.T ) {
167+ // REST fork: POST returns "already exists" error → treated as success.
168+ apiServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
169+ if r .Method == "POST" && strings .HasSuffix (r .URL .Path , "/fork" ) {
170+ w .WriteHeader (400 )
171+ _ , _ = w .Write ([]byte (`{"status":"Error","message":"database already exists"}` ))
172+ return
173+ }
174+ w .WriteHeader (404 )
175+ }))
176+ defer apiServer .Close ()
177+
178+ oldAPI := dolthubAPIBase
179+ dolthubAPIBase = apiServer .URL
180+ defer func () { dolthubAPIBase = oldAPI }()
181+
182+ provider := NewDoltHubProvider ("api-token" )
183+ err := provider .forkREST ("steveyegge" , "wl-commons" , "alice-dev" )
184+ if err != nil {
185+ t .Errorf ("forkREST should succeed for already-exists: %v" , err )
186+ }
187+ }
188+
189+ func TestDoltHubProvider_ForkREST_AuthError (t * testing.T ) {
190+ // REST fork: auth error → falls back to exists-check → ForkRequiredError.
191+ apiServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
192+ if r .Method == "POST" && strings .HasSuffix (r .URL .Path , "/fork" ) {
193+ w .WriteHeader (401 )
194+ _ , _ = w .Write ([]byte (`{"status":"Error","message":"unauthorized"}` ))
195+ return
196+ }
197+ // Exists-check for fallback: fork doesn't exist.
198+ w .WriteHeader (400 )
199+ _ , _ = w .Write ([]byte (`{"query_execution_status":"Error"}` ))
200+ }))
201+ defer apiServer .Close ()
202+
203+ oldAPI := dolthubAPIBase
204+ dolthubAPIBase = apiServer .URL
205+ defer func () { dolthubAPIBase = oldAPI }()
206+
207+ provider := NewDoltHubProvider ("bad-token" )
208+ err := provider .forkREST ("steveyegge" , "wl-commons" , "alice-dev" )
209+ if err == nil {
210+ t .Fatal ("expected ForkRequiredError, got nil" )
211+ }
212+ var forkErr * ForkRequiredError
213+ if ! errors .As (err , & forkErr ) {
214+ t .Fatalf ("expected ForkRequiredError, got %T: %v" , err , err )
215+ }
216+ }
217+
218+ func TestDoltHubProvider_Fork_NoSession_UsesREST (t * testing.T ) {
219+ // When no session token, Fork dispatches to forkREST (not ForkRequiredError).
220+ gotRESTFork := false
221+ apiServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
222+ if r .Method == "POST" && strings .HasSuffix (r .URL .Path , "/fork" ) {
223+ gotRESTFork = true
224+ w .WriteHeader (200 )
225+ _ , _ = w .Write ([]byte (`{"status":"Success","operation_name":""}` ))
226+ return
227+ }
228+ w .WriteHeader (404 )
229+ }))
230+ defer apiServer .Close ()
231+
232+ oldAPI := dolthubAPIBase
233+ dolthubAPIBase = apiServer .URL
234+ defer func () { dolthubAPIBase = oldAPI }()
235+
236+ t .Setenv ("DOLTHUB_SESSION_TOKEN" , "" )
237+
238+ provider := NewDoltHubProvider ("api-token" )
239+ err := provider .Fork ("steveyegge" , "wl-commons" , "alice-dev" )
240+ if err != nil {
241+ t .Errorf ("Fork should succeed via REST: %v" , err )
242+ }
243+ if ! gotRESTFork {
244+ t .Error ("expected Fork to use REST API, but no POST /fork was received" )
245+ }
246+ }
247+
115248func TestDoltHubProvider_Fork_NoSession_ForkExists (t * testing.T ) {
116- // When fork database already exists on DoltHub, Fork returns nil .
249+ // REST fork fails with auth error, but fork already exists → success .
117250 apiServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
251+ if r .Method == "POST" && strings .HasSuffix (r .URL .Path , "/fork" ) {
252+ w .WriteHeader (403 )
253+ _ , _ = w .Write ([]byte (`{"status":"Error","message":"forbidden"}` ))
254+ return
255+ }
256+ // Exists-check fallback: fork exists.
118257 if r .Header .Get ("authorization" ) != "token api-token" {
119258 t .Errorf ("expected auth header, got %q" , r .Header .Get ("authorization" ))
120259 }
@@ -137,8 +276,14 @@ func TestDoltHubProvider_Fork_NoSession_ForkExists(t *testing.T) {
137276}
138277
139278func TestDoltHubProvider_Fork_NoSession_ForkNotFound (t * testing.T ) {
140- // When fork database does not exist, Fork returns ForkRequiredError.
141- apiServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , _ * http.Request ) {
279+ // REST fork fails, fork doesn't exist → ForkRequiredError.
280+ apiServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
281+ if r .Method == "POST" && strings .HasSuffix (r .URL .Path , "/fork" ) {
282+ w .WriteHeader (403 )
283+ _ , _ = w .Write ([]byte (`{"status":"Error","message":"forbidden"}` ))
284+ return
285+ }
286+ // Exists-check fallback: fork not found.
142287 w .WriteHeader (400 )
143288 _ , _ = w .Write ([]byte (`{"query_execution_status":"Error","query_execution_message":"no such repository"}` ))
144289 }))
0 commit comments