import path from 'node:path'import { describe, expect, it, vi, beforeAll, afterEach } from 'vitest'import { getValue, setValue, setPort, setResourcePool, getValueFromPool, addValueToPool } from '../src/client.js'vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger')))vi.spyOn(global, 'fetch').mockImplementation((URL: string | URL | globalThis.Request, options?: RequestInit) => {const url = URL as stringconst body = options?.body as stringconst key = url.split('/').pop() || JSON.parse(body).keyif (key === 'fail') {return Promise.reject({message: 'Mock error'})}if (key === 'not-present') {return Promise.resolve(Response.json({ value: undefined }))}return Promise.resolve(Response.json({ value: 'store value' }))})const port = 3000const baseUrl = `http://localhost:${port}`const headers = {'Content-Type': 'application/json'}describe('client', () => {afterEach(() => {vi.mocked(fetch).mockClear()})describe('when used in launcher process', () => {it('Error should be thrown when attempting to retrieve a value before server initialization', () => {expect(getValue('*')).rejects.toThrowError('Attempting to use `getValue` before the server has been initialized.')expect(getValueFromPool('*')).rejects.toThrowError('Attempting to use `getValueFromPool` before the server has been initialized.')expect(addValueToPool('*', '')).rejects.toThrowError('Attempting to use `addValueToPool` before the server has been initialized.')})it('should not post before server has started', async () => {const result1 = await setValue('foo', 'bar')const result2 = await setValue('bar', 'foo')const result3 = await setResourcePool('foo', ['bar'])expect([result1, result2, result3]).toEqual([undefined, undefined, undefined])expect(fetch).toBeCalledTimes(0)setPort(port)await Promise.resolve()expect(vi.mocked(fetch)).toBeCalledTimes(3)for (let i = 1; i <= 3; i++) {expect(vi.mocked(fetch)).toHaveBeenNthCalledWith(i,expect.any(String),expect.objectContaining({ method: 'post' }))}await setValue('another', 'item')expect(vi.mocked(fetch).mockName('post')).toBeCalledTimes(4)for (let i = 1; i <= 4; i++) {expect(vi.mocked(fetch)).toHaveBeenNthCalledWith(i,expect.any(String),expect.objectContaining({ method: 'post' }))}})})describe('when used in worker process', () => {beforeAll(() => {setPort(port)})it('should set value', async () => {await setValue('foo', 'bar')expect(fetch).toBeCalledWith(`${baseUrl}/`, {method: 'post',body: JSON.stringify({ key: 'foo', value: 'bar' }),headers})})it('should get value', async () => {const result = await getValue('foo')expect(fetch).toBeCalledWith(`${baseUrl}/foo`, {method: 'get',headers})expect(result).toBe('store value')})it('should not fail if key is not in store', async () => {const result = await getValue('not-present')expect(fetch).toBeCalledWith(`${baseUrl}/not-present`, {method: 'get',headers})expect(result).toBeUndefined()})it('should throw an error on get error', async () => {await expect(() => getValue('fail')).rejects.toThrowError('Mock error')})it('should throw an error on set error', async () => {await expect(() => setValue('fail', 'fail')).rejects.toThrowError('Mock error')})})describe('when calling setResourcePool', () => {describe('and after setPort is called', () => {it("should call /pool/set and return it's response code", async () => {vi.mocked(fetch).mockResolvedValueOnce(Response.json({}, { status: 200 }))const result = await setResourcePool('foo', ['bar'])expect(fetch).toBeCalledWith(`${baseUrl}/pool`, {method: 'post',body: JSON.stringify({ key: 'foo', value: ['bar'] }),headers})expect(result).toEqual(200)})})describe('and when /pool/set fails', () => {it('should throw the error', async () => {vi.mocked(fetch).mockRejectedValue({ message: 'postError' })await expect(() => setResourcePool('fail', ['bar'])).rejects.toThrowError('postError')})})})describe('when calling getValueFromPool', () => {describe('and after setPort is called', () => {it("should call /pool/get and return it's response", async () => {vi.mocked(fetch).mockResolvedValueOnce(Response.json({ value: 'getResult' }))const result = await getValueFromPool('foo', { timeout: 100 })expect(fetch).toBeCalledWith(`${baseUrl}/pool/foo?timeout=100`, {method: 'get',headers})expect(result).toEqual('getResult')})})describe('and when /pool/get fails', () => {it('should throw the error', async () => {vi.mocked(fetch).mockRejectedValue({ message: 'getError' })await expect(() => getValueFromPool('foo')).rejects.toThrowError('getError')})})})describe('when calling addValueToPool', () => {describe('and after setPort is called', () => {it("should call /pool/add and return it's response code", async () => {vi.mocked(fetch).mockResolvedValueOnce(Response.json({}, { status: 200 }))const result = await addValueToPool('foo', 'bar')expect(fetch).toBeCalledWith(`${baseUrl}/pool/foo`, {method: 'post',body: JSON.stringify({ value: 'bar' }),headers })expect(result).toEqual(200)})})describe('and when /pool/add fails', () => {it('should throw the error', async () => {vi.mocked(fetch).mockRejectedValue({ message: 'postError' })await expect(() => addValueToPool('foo', {})).rejects.toThrowError('postError')})})})})