11import { describe , expect } from "bun:test"
22import { Effect } from "effect"
3+ import { HttpClient , HttpClientResponse } from "effect/unstable/http"
34import { Catalog } from "@opencode-ai/core/catalog"
45import { Credential } from "@opencode-ai/core/credential"
56import { EventV2 } from "@opencode-ai/core/event"
@@ -14,14 +15,16 @@ import { PluginTestLayer } from "./fixture"
1415
1516const it = testEffect ( PluginTestLayer )
1617
17- const addPlugin = Effect . fn ( function * ( ) {
18+ const addPlugin = Effect . fn ( function * ( http ?: HttpClient . HttpClient ) {
1819 const plugin = yield * PluginV2 . Service
1920 const host = yield * PluginHost . make ( plugin )
2021 const events = yield * EventV2 . Service
2122 const integration = yield * Integration . Service
23+ const client = yield * HttpClient . HttpClient
2224 yield * OpencodePlugin . effect ( host ) . pipe (
2325 Effect . provideService ( EventV2 . Service , events ) ,
2426 Effect . provideService ( Integration . Service , integration ) ,
27+ Effect . provideService ( HttpClient . HttpClient , http ?? client ) ,
2528 )
2629} )
2730
@@ -82,6 +85,63 @@ describe("OpencodePlugin", () => {
8285 } ) ,
8386 )
8487
88+ it . effect ( "resolves origin-rooted device verification URLs" , ( ) =>
89+ Effect . gen ( function * ( ) {
90+ const http = HttpClient . make ( ( request ) =>
91+ Effect . succeed (
92+ HttpClientResponse . fromWeb (
93+ request ,
94+ Response . json ( {
95+ device_code : "device" ,
96+ user_code : "user" ,
97+ verification_uri_complete : "/console/device?user_code=user&client_id=opencode-cli" ,
98+ expires_in : 60 ,
99+ interval : 60 ,
100+ } ) ,
101+ ) ,
102+ ) ,
103+ )
104+ yield * addPlugin ( http )
105+ const integration = yield * Integration . Service
106+ const attempt = yield * integration . connection . oauth ( {
107+ integrationID : Integration . ID . make ( "opencode" ) ,
108+ methodID : Integration . MethodID . make ( "device" ) ,
109+ inputs : { } ,
110+ } )
111+ expect ( attempt . url ) . toBe ( "https://opencode.ai/console/device?user_code=user&client_id=opencode-cli" )
112+ } ) ,
113+ )
114+
115+ it . effect ( "rejects malformed device verification URLs" , ( ) =>
116+ Effect . gen ( function * ( ) {
117+ const http = HttpClient . make ( ( request ) =>
118+ Effect . succeed (
119+ HttpClientResponse . fromWeb (
120+ request ,
121+ Response . json ( {
122+ device_code : "device" ,
123+ user_code : "user" ,
124+ verification_uri_complete : "http://[::1" ,
125+ expires_in : 60 ,
126+ interval : 60 ,
127+ } ) ,
128+ ) ,
129+ ) ,
130+ )
131+ yield * addPlugin ( http )
132+ const integration = yield * Integration . Service
133+ const error = yield * integration . connection
134+ . oauth ( {
135+ integrationID : Integration . ID . make ( "opencode" ) ,
136+ methodID : Integration . MethodID . make ( "device" ) ,
137+ inputs : { } ,
138+ } )
139+ . pipe ( Effect . flip )
140+ expect ( error ) . toBeInstanceOf ( Integration . AuthorizationError )
141+ expect ( String ( error . cause ) ) . toContain ( "Invalid device verification URL" )
142+ } ) ,
143+ )
144+
85145 it . live ( "loads providers and models from the connected OpenCode server" , ( ) =>
86146 Effect . acquireUseRelease (
87147 Effect . sync ( ( ) => {
0 commit comments