@@ -5,6 +5,7 @@ import { Conversation } from '../src/conversation';
55import { startServer } from '../src/server' ;
66import { ResourceRegistry } from '../src/providers/index' ;
77import { FsProvider } from '../src/providers/fs' ;
8+ import { PermissionManager } from '../src/permissions' ;
89import { DEFAULT_CONFIG } from '../src/types' ;
910import { mkdtempSync , writeFileSync , mkdirSync } from 'fs' ;
1011import { tmpdir } from 'os' ;
@@ -21,7 +22,8 @@ const config = {
2122} ;
2223
2324let baseUrl : string ;
24- let handle : { port : number ; stop : ( ) => void } ;
25+ let handle : { port : number ; token : string ; stop : ( ) => void } ;
26+ let authHeaders : Record < string , string > ;
2527let conversation : Conversation ;
2628let resources : ResourceRegistry ;
2729let origCwd : string ;
@@ -34,8 +36,12 @@ beforeAll(async () => {
3436 resources = new ResourceRegistry ( ) ;
3537 resources . register ( new FsProvider ( { roots : [ tmpDir ] , allowWrite : true , maxReadSize : 1024 * 1024 } ) ) ;
3638 await resources . initAll ( ) ;
37- handle = startServer ( conversation , config , resources ) ;
38- baseUrl = `http://localhost:${ handle . port } ` ;
39+ const perms = new PermissionManager ( tmpDir ) ;
40+ // Allow write for tests
41+ perms . addGlobalRule ( { provider : 'fs' , action : 'write' , allowed : true } ) ;
42+ handle = startServer ( conversation , config , resources , perms ) ;
43+ baseUrl = `http://127.0.0.1:${ handle . port } ` ;
44+ authHeaders = { 'Authorization' : `Bearer ${ handle . token } ` } ;
3945} ) ;
4046
4147afterAll ( ( ) => {
@@ -59,7 +65,7 @@ describe('conversation API', () => {
5965 test ( 'POST /api/message requires content' , async ( ) => {
6066 const resp = await fetch ( `${ baseUrl } /api/message` , {
6167 method : 'POST' ,
62- headers : { 'Content-Type' : 'application/json' } ,
68+ headers : { 'Content-Type' : 'application/json' , ... authHeaders } ,
6369 body : JSON . stringify ( { } ) ,
6470 } ) ;
6571 expect ( resp . status ) . toBe ( 400 ) ;
@@ -68,7 +74,7 @@ describe('conversation API', () => {
6874 test ( 'POST /api/message accepts valid input' , async ( ) => {
6975 const resp = await fetch ( `${ baseUrl } /api/message` , {
7076 method : 'POST' ,
71- headers : { 'Content-Type' : 'application/json' } ,
77+ headers : { 'Content-Type' : 'application/json' , ... authHeaders } ,
7278 body : JSON . stringify ( { content : 'hello from test' } ) ,
7379 } ) ;
7480 expect ( resp . status ) . toBe ( 200 ) ;
@@ -81,7 +87,7 @@ describe('conversation API', () => {
8187 test ( 'POST /api/context switches context' , async ( ) => {
8288 const resp = await fetch ( `${ baseUrl } /api/context` , {
8389 method : 'POST' ,
84- headers : { 'Content-Type' : 'application/json' } ,
90+ headers : { 'Content-Type' : 'application/json' , ... authHeaders } ,
8591 body : JSON . stringify ( { context : 'personal' } ) ,
8692 } ) ;
8793 expect ( resp . status ) . toBe ( 200 ) ;
@@ -92,26 +98,26 @@ describe('conversation API', () => {
9298 test ( 'POST /api/context rejects invalid' , async ( ) => {
9399 const resp = await fetch ( `${ baseUrl } /api/context` , {
94100 method : 'POST' ,
95- headers : { 'Content-Type' : 'application/json' } ,
101+ headers : { 'Content-Type' : 'application/json' , ... authHeaders } ,
96102 body : JSON . stringify ( { context : 'invalid' } ) ,
97103 } ) ;
98104 expect ( resp . status ) . toBe ( 400 ) ;
99105 } ) ;
100106
101107 test ( 'GET /api/stats returns stats' , async ( ) => {
102- const resp = await fetch ( `${ baseUrl } /api/stats` ) ;
108+ const resp = await fetch ( `${ baseUrl } /api/stats` , { headers : authHeaders } ) ;
103109 expect ( resp . status ) . toBe ( 200 ) ;
104110 const body = await resp . json ( ) as Record < string , unknown > ;
105111 expect ( typeof body . total ) . toBe ( 'number' ) ;
106112 } ) ;
107113
108114 test ( 'GET /api/search requires q param' , async ( ) => {
109- const resp = await fetch ( `${ baseUrl } /api/search` ) ;
115+ const resp = await fetch ( `${ baseUrl } /api/search` , { headers : authHeaders } ) ;
110116 expect ( resp . status ) . toBe ( 400 ) ;
111117 } ) ;
112118
113119 test ( 'GET /api/search returns results' , async ( ) => {
114- const resp = await fetch ( `${ baseUrl } /api/search?q=hello` ) ;
120+ const resp = await fetch ( `${ baseUrl } /api/search?q=hello` , { headers : authHeaders } ) ;
115121 expect ( resp . status ) . toBe ( 200 ) ;
116122 const body = await resp . json ( ) as { results : unknown [ ] ; count : number } ;
117123 expect ( Array . isArray ( body . results ) ) . toBe ( true ) ;
@@ -120,7 +126,7 @@ describe('conversation API', () => {
120126
121127describe ( 'resource API' , ( ) => {
122128 test ( 'GET /api/resources lists providers' , async ( ) => {
123- const resp = await fetch ( `${ baseUrl } /api/resources` ) ;
129+ const resp = await fetch ( `${ baseUrl } /api/resources` , { headers : authHeaders } ) ;
124130 expect ( resp . status ) . toBe ( 200 ) ;
125131 const body = await resp . json ( ) as { providers : { name : string } [ ] } ;
126132 expect ( body . providers . length ) . toBeGreaterThan ( 0 ) ;
@@ -130,7 +136,7 @@ describe('resource API', () => {
130136 test ( 'POST /api/resources/fs/list works' , async ( ) => {
131137 const resp = await fetch ( `${ baseUrl } /api/resources/fs/list` , {
132138 method : 'POST' ,
133- headers : { 'Content-Type' : 'application/json' } ,
139+ headers : { 'Content-Type' : 'application/json' , ... authHeaders } ,
134140 body : JSON . stringify ( { path : tmpDir } ) ,
135141 } ) ;
136142 expect ( resp . status ) . toBe ( 200 ) ;
@@ -145,7 +151,7 @@ describe('resource API', () => {
145151
146152 const resp = await fetch ( `${ baseUrl } /api/resources/fs/read` , {
147153 method : 'POST' ,
148- headers : { 'Content-Type' : 'application/json' } ,
154+ headers : { 'Content-Type' : 'application/json' , ... authHeaders } ,
149155 body : JSON . stringify ( { path : testFile } ) ,
150156 } ) ;
151157 expect ( resp . status ) . toBe ( 200 ) ;
@@ -157,7 +163,7 @@ describe('resource API', () => {
157163 const testFile = join ( tmpDir , 'test-write.txt' ) ;
158164 const resp = await fetch ( `${ baseUrl } /api/resources/fs/write` , {
159165 method : 'POST' ,
160- headers : { 'Content-Type' : 'application/json' } ,
166+ headers : { 'Content-Type' : 'application/json' , ... authHeaders } ,
161167 body : JSON . stringify ( { path : testFile , content : 'written by daemon' } ) ,
162168 } ) ;
163169 expect ( resp . status ) . toBe ( 200 ) ;
@@ -170,16 +176,21 @@ describe('resource API', () => {
170176 test ( 'POST /api/resources/unknown/action returns 500' , async ( ) => {
171177 const resp = await fetch ( `${ baseUrl } /api/resources/nonexistent/action` , {
172178 method : 'POST' ,
173- headers : { 'Content-Type' : 'application/json' } ,
179+ headers : { 'Content-Type' : 'application/json' , ... authHeaders } ,
174180 body : JSON . stringify ( { } ) ,
175181 } ) ;
176182 expect ( resp . status ) . toBe ( 500 ) ;
177183 } ) ;
178184} ) ;
179185
180186describe ( '404 handling' , ( ) => {
187+ test ( 'unauthenticated request returns 401' , async ( ) => {
188+ const resp = await fetch ( `${ baseUrl } /api/stats` ) ;
189+ expect ( resp . status ) . toBe ( 401 ) ;
190+ } ) ;
191+
181192 test ( 'unknown route returns 404 with route list' , async ( ) => {
182- const resp = await fetch ( `${ baseUrl } /api/nonexistent` ) ;
193+ const resp = await fetch ( `${ baseUrl } /api/nonexistent` , { headers : authHeaders } ) ;
183194 expect ( resp . status ) . toBe ( 404 ) ;
184195 const body = await resp . json ( ) as { routes : string [ ] } ;
185196 expect ( Array . isArray ( body . routes ) ) . toBe ( true ) ;
0 commit comments