// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========

import { describe, expect, it } from 'vitest'
import { TokenManager } from './oauth.ts'

class FakeManager extends TokenManager {
  calls = 0
  constructor(
    private readonly expiresIn: number,
    bufferSeconds: number,
  ) {
    super(bufferSeconds)
  }

  protected refreshPair(): Promise<[string, number]> {
    this.calls += 1
    return Promise.resolve([`tok${String(this.calls)}`, this.expiresIn])
  }
}

describe('TokenManager', () => {
  it('caches until expiry', async () => {
    const tm = new FakeManager(3600, 300)
    expect(await tm.getToken()).toBe('tok1')
    expect(await tm.getToken()).toBe('tok1')
    expect(tm.calls).toBe(1)
  })

  it('the buffer refreshes early', async () => {
    // 200s of lifetime minus a 300s buffer is already expired, so every
    // call refreshes.
    const tm = new FakeManager(200, 300)
    expect(await tm.getToken()).toBe('tok1')
    expect(await tm.getToken()).toBe('tok2')
    expect(tm.calls).toBe(2)
  })

  it('concurrent callers share one refresh', async () => {
    const tm = new FakeManager(3600, 300)
    const tokens = await Promise.all([tm.getToken(), tm.getToken(), tm.getToken()])
    expect(tokens).toEqual(['tok1', 'tok1', 'tok1'])
    expect(tm.calls).toBe(1)
  })
})
