Skip to content

Commit 69de8f4

Browse files
committed
Add testcase
1 parent 629fa55 commit 69de8f4

File tree

3 files changed

+271
-1
lines changed

3 files changed

+271
-1
lines changed

bunfig.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
coverage = true
33
coveragePathIgnorePatterns = ["node_modules", "**/dist/**"]
44
coverageSkipTestFiles = true
5-
preload = ["bun-test-env-dom"]
5+
preload = ["bun-test-env-dom"]
6+
coverageThreshold = 1.0

packages/generator/src/__tests__/__snapshots__/generate-interface.test.ts.snap

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1638,3 +1638,117 @@ declare module "@devup-api/fetch" {
16381638
interface DevupErrorComponentStruct {}
16391639
}"
16401640
`;
1641+
1642+
exports[`generateInterface handles circular references in schema collection 1`] = `
1643+
"import "@devup-api/fetch";
1644+
import type { DevupObject } from "@devup-api/fetch";
1645+
1646+
declare module "@devup-api/fetch" {
1647+
interface DevupApiServers {
1648+
'openapi.json': never
1649+
}
1650+
1651+
interface DevupGetApiStruct {
1652+
'openapi.json': {
1653+
'/nodes': {
1654+
response: Array<DevupObject<'response', 'openapi.json'>['TreeNode']>;
1655+
};
1656+
listNodes: {
1657+
response: Array<DevupObject<'response', 'openapi.json'>['TreeNode']>;
1658+
};
1659+
}
1660+
}
1661+
1662+
interface DevupRequestComponentStruct {}
1663+
1664+
interface DevupResponseComponentStruct {
1665+
'openapi.json': {
1666+
TreeNode: {
1667+
id?: number;
1668+
name?: string;
1669+
children?: Array<DevupObject<'response', 'openapi.json'>['TreeNode']>;
1670+
};
1671+
}
1672+
}
1673+
1674+
interface DevupErrorComponentStruct {}
1675+
}"
1676+
`;
1677+
1678+
exports[`generateInterface handles request schema with inline enum 1`] = `
1679+
"import "@devup-api/fetch";
1680+
import type { DevupObject } from "@devup-api/fetch";
1681+
1682+
declare module "@devup-api/fetch" {
1683+
type CreateItemRequestPriority = "low" | "medium" | "high"
1684+
1685+
interface DevupApiServers {
1686+
'openapi.json': never
1687+
}
1688+
1689+
interface DevupPostApiStruct {
1690+
'openapi.json': {
1691+
'/items': {
1692+
body: DevupObject<'request', 'openapi.json'>['CreateItemRequest'];
1693+
response?: {};
1694+
};
1695+
createItem: {
1696+
body: DevupObject<'request', 'openapi.json'>['CreateItemRequest'];
1697+
response?: {};
1698+
};
1699+
}
1700+
}
1701+
1702+
interface DevupRequestComponentStruct {
1703+
'openapi.json': {
1704+
CreateItemRequest: {
1705+
name?: string;
1706+
priority?: CreateItemRequestPriority;
1707+
};
1708+
}
1709+
}
1710+
1711+
interface DevupResponseComponentStruct {}
1712+
1713+
interface DevupErrorComponentStruct {}
1714+
}"
1715+
`;
1716+
1717+
exports[`generateInterface handles error schema with inline enum 1`] = `
1718+
"import "@devup-api/fetch";
1719+
import type { DevupObject } from "@devup-api/fetch";
1720+
1721+
declare module "@devup-api/fetch" {
1722+
type ErrorResponseCode = "INVALID_INPUT" | "NOT_FOUND" | "UNAUTHORIZED"
1723+
1724+
interface DevupApiServers {
1725+
'openapi.json': never
1726+
}
1727+
1728+
interface DevupGetApiStruct {
1729+
'openapi.json': {
1730+
'/items': {
1731+
response?: {};
1732+
error: DevupObject<'error', 'openapi.json'>['ErrorResponse'];
1733+
};
1734+
getItems: {
1735+
response?: {};
1736+
error: DevupObject<'error', 'openapi.json'>['ErrorResponse'];
1737+
};
1738+
}
1739+
}
1740+
1741+
interface DevupRequestComponentStruct {}
1742+
1743+
interface DevupResponseComponentStruct {}
1744+
1745+
interface DevupErrorComponentStruct {
1746+
'openapi.json': {
1747+
ErrorResponse: {
1748+
message?: string;
1749+
code?: ErrorResponseCode;
1750+
};
1751+
}
1752+
}
1753+
}"
1754+
`;

packages/generator/src/__tests__/generate-interface.test.ts

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,3 +1899,158 @@ test('generateInterface handles inline response with nested $ref containing enum
18991899
"DevupObject<'response', 'openapi.json'>['OtherItemResponse']",
19001900
)
19011901
})
1902+
1903+
// Test circular reference handling in collectSchemaNames (line 83 coverage)
1904+
test('generateInterface handles circular references in schema collection', () => {
1905+
const schema = {
1906+
paths: {
1907+
'/nodes': {
1908+
get: {
1909+
operationId: 'listNodes',
1910+
responses: {
1911+
'200': {
1912+
description: 'Success',
1913+
content: {
1914+
'application/json': {
1915+
schema: {
1916+
type: 'array',
1917+
items: {
1918+
$ref: '#/components/schemas/TreeNode',
1919+
},
1920+
},
1921+
},
1922+
},
1923+
},
1924+
},
1925+
},
1926+
},
1927+
},
1928+
components: {
1929+
schemas: {
1930+
TreeNode: {
1931+
type: 'object',
1932+
properties: {
1933+
id: { type: 'number' },
1934+
name: { type: 'string' },
1935+
// Circular reference - children contains TreeNode
1936+
children: {
1937+
type: 'array',
1938+
items: {
1939+
$ref: '#/components/schemas/TreeNode',
1940+
},
1941+
},
1942+
},
1943+
},
1944+
},
1945+
},
1946+
}
1947+
const result = generateInterface(createSchemas(createDocument(schema as any)))
1948+
expect(result).toMatchSnapshot()
1949+
// Should not cause infinite loop and should generate valid output
1950+
expect(result).toContain('TreeNode')
1951+
})
1952+
1953+
// Test request schema with inline enum (lines 659-661 coverage)
1954+
test('generateInterface handles request schema with inline enum', () => {
1955+
const schema = {
1956+
paths: {
1957+
'/items': {
1958+
post: {
1959+
operationId: 'createItem',
1960+
requestBody: {
1961+
content: {
1962+
'application/json': {
1963+
schema: {
1964+
$ref: '#/components/schemas/CreateItemRequest',
1965+
},
1966+
},
1967+
},
1968+
},
1969+
responses: {
1970+
'200': {
1971+
description: 'Success',
1972+
content: {
1973+
'application/json': {
1974+
schema: { type: 'object', properties: {} },
1975+
},
1976+
},
1977+
},
1978+
},
1979+
},
1980+
},
1981+
},
1982+
components: {
1983+
schemas: {
1984+
CreateItemRequest: {
1985+
type: 'object',
1986+
properties: {
1987+
name: { type: 'string' },
1988+
// Inline enum - not a $ref, should be collected during schema processing
1989+
priority: {
1990+
type: 'string',
1991+
enum: ['low', 'medium', 'high'],
1992+
},
1993+
},
1994+
},
1995+
},
1996+
},
1997+
}
1998+
const result = generateInterface(createSchemas(createDocument(schema as any)))
1999+
expect(result).toMatchSnapshot()
2000+
// Inline enum should generate type alias based on context
2001+
expect(result).toContain('type CreateItemRequestPriority =')
2002+
expect(result).toContain('"low"')
2003+
})
2004+
2005+
// Test error schema with inline enum (lines 705-707 coverage)
2006+
test('generateInterface handles error schema with inline enum', () => {
2007+
const schema = {
2008+
paths: {
2009+
'/items': {
2010+
get: {
2011+
operationId: 'getItems',
2012+
responses: {
2013+
'200': {
2014+
description: 'Success',
2015+
content: {
2016+
'application/json': {
2017+
schema: { type: 'object', properties: {} },
2018+
},
2019+
},
2020+
},
2021+
'400': {
2022+
description: 'Error',
2023+
content: {
2024+
'application/json': {
2025+
schema: {
2026+
$ref: '#/components/schemas/ErrorResponse',
2027+
},
2028+
},
2029+
},
2030+
},
2031+
},
2032+
},
2033+
},
2034+
},
2035+
components: {
2036+
schemas: {
2037+
ErrorResponse: {
2038+
type: 'object',
2039+
properties: {
2040+
message: { type: 'string' },
2041+
// Inline enum - not a $ref, should be collected during schema processing
2042+
code: {
2043+
type: 'string',
2044+
enum: ['INVALID_INPUT', 'NOT_FOUND', 'UNAUTHORIZED'],
2045+
},
2046+
},
2047+
},
2048+
},
2049+
},
2050+
}
2051+
const result = generateInterface(createSchemas(createDocument(schema as any)))
2052+
expect(result).toMatchSnapshot()
2053+
// Inline enum should generate type alias based on context
2054+
expect(result).toContain('type ErrorResponseCode =')
2055+
expect(result).toContain('"INVALID_INPUT"')
2056+
})

0 commit comments

Comments
 (0)