@@ -2,87 +2,27 @@ import { test } from 'node:test';
22import assert from 'node:assert' ;
33import sinon from 'sinon' ;
44import './setup.js' ;
5-
6- // Mock the models by setting up stubs directly
7- const mockUser = {
8- findById : sinon . stub ( ) ,
9- } ;
10-
11- const mockRepository = {
12- findByOwnerAndRepo : sinon . stub ( ) ,
13- } ;
14-
15- // We'll override the imports in the Auth module by mocking the module loading
16- const originalImport = await import ( '../src/helpers/auth.js' ) ;
17-
18- // Create a test-specific Auth class that uses our mocks
19- class TestAuth extends originalImport . Auth {
20- constructor ( options ) {
21- super ( options ) ;
22- // Override the models used by the Auth class
23- this . _testRepository = mockRepository ;
24- }
25-
26- async getAllMethods ( ) {
27- // Copy the original method but use our mocked models
28- if ( this . _methods ) return this . _methods ;
29-
30- this . _methods = [ ] ;
31-
32- // Priority 1: Repository GitHub App authentication
33- try {
34- const repository = await this . _testRepository . findByOwnerAndRepo (
35- this . owner ,
36- this . repo
37- ) ;
38-
39- if ( repository && repository . configured && repository . installationId ) {
40- this . _methods . push ( {
41- type : 'APP' ,
42- installationId : repository . installationId ,
43- priority : 1 ,
44- description : 'Repository GitHub App' ,
45- } ) ;
46- }
47- } catch ( error ) {
48- console . warn ( 'Failed to load repository config:' , error . message ) ;
49- }
50-
51- // Priority 2: Environment token fallback (if available)
52- if ( process . env . GITHUB_FALLBACK_TOKEN ) {
53- this . _methods . push ( {
54- type : 'ENV' ,
55- token : process . env . GITHUB_FALLBACK_TOKEN ,
56- priority : 2 ,
57- description : 'Environment fallback token' ,
58- } ) ;
59- }
60-
61- // Sort by priority and return
62- this . _methods . sort ( ( a , b ) => a . priority - b . priority ) ;
63- return this . _methods ;
64- }
65- }
5+ import { Repository } from '../src/database/models.js' ;
6+ import { Auth } from '../src/helpers/auth.js' ;
667
678test ( 'Auth class' , async t => {
9+ let findByOwnerAndRepoStub ;
10+
6811 t . beforeEach ( ( ) => {
69- // Reset all stubs before each test
70- mockUser . findById . reset ( ) ;
71- mockRepository . findByOwnerAndRepo . reset ( ) ;
12+ // Mock Repository.findByOwnerAndRepo at module level
13+ findByOwnerAndRepoStub = sinon . stub ( Repository , 'findByOwnerAndRepo' ) ;
7214
7315 // Clear environment variable
7416 delete process . env . GITHUB_FALLBACK_TOKEN ;
7517 } ) ;
7618
77- t . after ( ( ) => {
78- // Clear any remaining timers or handles
79- if ( global . gc ) {
80- global . gc ( ) ;
81- }
19+ t . afterEach ( ( ) => {
20+ // Restore original implementation
21+ findByOwnerAndRepoStub . restore ( ) ;
8222 } ) ;
8323
8424 await t . test ( 'should create auth with minimal parameters' , async ( ) => {
85- const auth = new TestAuth ( { owner : 'test' , repo : 'repo' } ) ;
25+ const auth = new Auth ( { owner : 'test' , repo : 'repo' } ) ;
8626 assert . ok ( auth ) ;
8727 assert . strictEqual ( auth . owner , 'test' ) ;
8828 assert . strictEqual ( auth . repo , 'repo' ) ;
@@ -91,9 +31,9 @@ test('Auth class', async t => {
9131 await t . test (
9232 'should return empty methods when no auth available' ,
9333 async ( ) => {
94- mockRepository . findByOwnerAndRepo . resolves ( null ) ;
34+ findByOwnerAndRepoStub . resolves ( null ) ;
9535
96- const auth = new TestAuth ( { owner : 'test' , repo : 'repo' } ) ;
36+ const auth = new Auth ( { owner : 'test' , repo : 'repo' } ) ;
9737 const methods = await auth . getAllMethods ( ) ;
9838
9939 assert . strictEqual ( methods . length , 0 ) ;
@@ -102,13 +42,15 @@ test('Auth class', async t => {
10242 ) ;
10343
10444 await t . test ( 'should prioritize GitHub App when available' , async ( ) => {
45+ // Mock repository WITHOUT 'configured' field (matches actual schema after PR #306)
10546 const mockRepo = {
106- configured : true ,
47+ owner : 'test' ,
48+ repo : 'repo' ,
10749 installationId : 12345 ,
10850 } ;
109- mockRepository . findByOwnerAndRepo . resolves ( mockRepo ) ;
51+ findByOwnerAndRepoStub . resolves ( mockRepo ) ;
11052
111- const auth = new TestAuth ( { owner : 'test' , repo : 'repo' } ) ;
53+ const auth = new Auth ( { owner : 'test' , repo : 'repo' } ) ;
11254 const methods = await auth . getAllMethods ( ) ;
11355
11456 assert . strictEqual ( methods . length , 1 ) ;
@@ -119,14 +61,16 @@ test('Auth class', async t => {
11961 } ) ;
12062
12163 await t . test ( 'should use both GitHub App and fallback token' , async ( ) => {
64+ // Mock repository WITHOUT 'configured' field
12265 const mockRepo = {
123- configured : true ,
66+ owner : 'test' ,
67+ repo : 'repo' ,
12468 installationId : 12345 ,
12569 } ;
126- mockRepository . findByOwnerAndRepo . resolves ( mockRepo ) ;
70+ findByOwnerAndRepoStub . resolves ( mockRepo ) ;
12771 process . env . GITHUB_FALLBACK_TOKEN = 'env-token' ;
12872
129- const auth = new TestAuth ( { owner : 'test' , repo : 'repo' } ) ;
73+ const auth = new Auth ( { owner : 'test' , repo : 'repo' } ) ;
13074 const methods = await auth . getAllMethods ( ) ;
13175
13276 assert . strictEqual ( methods . length , 2 ) ;
@@ -138,9 +82,9 @@ test('Auth class', async t => {
13882
13983 await t . test ( 'should add environment token when available' , async ( ) => {
14084 process . env . GITHUB_FALLBACK_TOKEN = 'env-token' ;
141- mockRepository . findByOwnerAndRepo . resolves ( null ) ;
85+ findByOwnerAndRepoStub . resolves ( null ) ;
14286
143- const auth = new TestAuth ( { owner : 'test' , repo : 'repo' } ) ;
87+ const auth = new Auth ( { owner : 'test' , repo : 'repo' } ) ;
14488 const methods = await auth . getAllMethods ( ) ;
14589
14690 assert . strictEqual ( methods . length , 1 ) ;
@@ -151,14 +95,16 @@ test('Auth class', async t => {
15195 } ) ;
15296
15397 await t . test ( 'should provide auth strategy description' , async ( ) => {
98+ // Mock repository WITHOUT 'configured' field
15499 const mockRepo = {
155- configured : true ,
100+ owner : 'test' ,
101+ repo : 'repo' ,
156102 installationId : 12345 ,
157103 } ;
158- mockRepository . findByOwnerAndRepo . resolves ( mockRepo ) ;
104+ findByOwnerAndRepoStub . resolves ( mockRepo ) ;
159105 process . env . GITHUB_FALLBACK_TOKEN = 'env-token' ;
160106
161- const auth = new TestAuth ( { owner : 'test' , repo : 'repo' } ) ;
107+ const auth = new Auth ( { owner : 'test' , repo : 'repo' } ) ;
162108 const strategy = await auth . getAuthStrategy ( ) ;
163109
164110 assert . ok ( strategy . includes ( 'Auth strategy (with fallbacks)' ) ) ;
@@ -167,20 +113,63 @@ test('Auth class', async t => {
167113 } ) ;
168114
169115 await t . test ( 'should cache methods on repeated calls' , async ( ) => {
116+ // Mock repository WITHOUT 'configured' field
170117 const mockRepo = {
171- configured : true ,
118+ owner : 'test' ,
119+ repo : 'repo' ,
172120 installationId : 12345 ,
173121 } ;
174- mockRepository . findByOwnerAndRepo . resolves ( mockRepo ) ;
122+ findByOwnerAndRepoStub . resolves ( mockRepo ) ;
175123
176- const auth = new TestAuth ( { owner : 'test' , repo : 'repo' } ) ;
124+ const auth = new Auth ( { owner : 'test' , repo : 'repo' } ) ;
177125
178126 // First call
179127 const methods1 = await auth . getAllMethods ( ) ;
180128 // Second call should use cache
181129 const methods2 = await auth . getAllMethods ( ) ;
182130
183131 assert . strictEqual ( methods1 , methods2 ) ; // Same reference
184- assert . strictEqual ( mockRepository . findByOwnerAndRepo . callCount , 1 ) ; // Only called once
132+ assert . strictEqual ( findByOwnerAndRepoStub . callCount , 1 ) ; // Only called once
185133 } ) ;
134+
135+ await t . test ( 'should handle repository without installationId' , async ( ) => {
136+ // Repository exists but has no installationId
137+ const mockRepo = {
138+ owner : 'test' ,
139+ repo : 'repo' ,
140+ // No installationId
141+ } ;
142+ findByOwnerAndRepoStub . resolves ( mockRepo ) ;
143+
144+ const auth = new Auth ( { owner : 'test' , repo : 'repo' } ) ;
145+ const methods = await auth . getAllMethods ( ) ;
146+
147+ // Should have no GitHub App method
148+ assert . strictEqual ( methods . length , 0 ) ;
149+ assert . strictEqual ( await auth . hasValidAuth ( ) , false ) ;
150+ } ) ;
151+
152+ await t . test (
153+ 'REGRESSION: should work with installationId but no configured field' ,
154+ async ( ) => {
155+ // This is the exact scenario that caused the bug:
156+ // After PR #306, 'configured' field was removed from schema
157+ // Auth class was still checking for it, causing all repos to fail
158+ const mockRepo = {
159+ owner : 'test' ,
160+ repo : 'repo' ,
161+ installationId : 12345 ,
162+ // NOTE: No 'configured' field - this matches the actual schema
163+ } ;
164+ findByOwnerAndRepoStub . resolves ( mockRepo ) ;
165+
166+ const auth = new Auth ( { owner : 'test' , repo : 'repo' } ) ;
167+ const methods = await auth . getAllMethods ( ) ;
168+
169+ // Should work correctly - the fix removed the configured check
170+ assert . strictEqual ( methods . length , 1 ) ;
171+ assert . strictEqual ( methods [ 0 ] . type , 'APP' ) ;
172+ assert . strictEqual ( methods [ 0 ] . installationId , 12345 ) ;
173+ }
174+ ) ;
186175} ) ;
0 commit comments