claude overtook and fixes

This commit is contained in:
JiriUhlir
2026-06-29 10:17:14 +02:00
parent 5281d56998
commit 04fbf692a3
6 changed files with 151 additions and 6 deletions
+27 -1
View File
@@ -42,6 +42,29 @@ describe("SessionManager", () => {
expect(sessionManager.buildCookieHeader(session)).toBe("B1SESSION=abc123; ROUTEID=.node1");
});
it("sends the language as an integer code to the Login action", async () => {
const http = {
post: vi.fn().mockResolvedValue({
data: { SessionId: "abc123", SessionTimeout: 30 },
headers: {}
})
};
const sessionManager = new SessionManager(http as never, { ...config, language: "3" });
await sessionManager.getSession();
expect(http.post).toHaveBeenCalledWith(
"/Login",
{
CompanyDB: "SBODEMOUS",
UserName: "manager",
Password: "secret",
Language: 3
},
{ skipAuth: true }
);
});
it("logs out and clears the current session", async () => {
const http = {
post: vi
@@ -57,7 +80,10 @@ describe("SessionManager", () => {
await sessionManager.getSession();
await sessionManager.logout();
expect(http.post).toHaveBeenLastCalledWith("/Logout", undefined, { skipAuth: false });
expect(http.post).toHaveBeenLastCalledWith("/Logout", undefined, {
skipAuth: true,
headers: { Cookie: "B1SESSION=abc123" }
});
expect(sessionManager.currentSession).toBeUndefined();
});
});
+67
View File
@@ -84,4 +84,71 @@ describe("SapB1Client", () => {
expect(loginCalls).toBe(2);
expect(resourceCalls).toBe(2);
});
it("re-logins after a 401 even when retries are disabled", async () => {
let resourceCalls = 0;
let loginCalls = 0;
const http = axios.create({
adapter: async (requestConfig) => {
if (requestConfig.url === "/Login") {
loginCalls += 1;
return {
...response({ SessionId: `session-${loginCalls}`, SessionTimeout: 30 }, requestConfig),
headers: { "set-cookie": [`B1SESSION=session-${loginCalls}; Path=/b1s/v1`] }
};
}
resourceCalls += 1;
if (resourceCalls === 1) {
return Promise.reject({
isAxiosError: true,
message: "Unauthorized",
response: { status: 401, data: { error: { message: { value: "Session expired" } } } }
});
}
return response({ value: [] }, requestConfig);
}
});
const client = new SapB1Client({ ...config, retryCount: 0 }, { http });
await expect(client.get("Items")).resolves.toEqual({ value: [] });
expect(loginCalls).toBe(2);
expect(resourceCalls).toBe(2);
});
it("follows an absolute nextLink while preserving its query string", async () => {
const requestedUrls: string[] = [];
const http = axios.create({
adapter: async (requestConfig) => {
if (requestConfig.url === "/Login") {
return {
...response({ SessionId: "abc123", SessionTimeout: 30 }, requestConfig),
headers: { "set-cookie": ["B1SESSION=abc123; Path=/b1s/v1"] }
};
}
requestedUrls.push(String(requestConfig.url));
if (requestConfig.url === "Items") {
return response(
{
value: [{ ItemCode: "A1" }],
"odata.nextLink": "https://sap.example.local:50000/b1s/v1/Items?$skip=20"
},
requestConfig
);
}
return response({ value: [{ ItemCode: "A2" }] }, requestConfig);
}
});
const client = new SapB1Client(config, { http });
const all = await client.getAll<{ ItemCode: string }>("Items");
expect(all).toEqual([{ ItemCode: "A1" }, { ItemCode: "A2" }]);
expect(requestedUrls).toEqual(["Items", "Items?$skip=20"]);
});
});