@xapi-js/adaptor-nestjs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +29 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +49 -0
- package/coverage/coverage-final.json +3 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +131 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/coverage/xapi-request-interceptor.ts.html +133 -0
- package/coverage/xapi-response-interceptor.ts.html +169 -0
- package/package.json +42 -0
- package/src/xapi-request-interceptor.ts +31 -0
- package/src/xapi-response-interceptor.ts +43 -0
- package/test/xapi-request-interceptor.test.ts +115 -0
- package/test/xapi-response-interceptor.test.ts +113 -0
- package/vitest.config.ts +16 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { of, firstValueFrom } from 'rxjs';
|
|
3
|
+
import { XapiRequestInterceptor } from '../src/xapi-request-interceptor';
|
|
4
|
+
import { CallHandler, ExecutionContext } from '@nestjs/common';
|
|
5
|
+
import { XapiRoot } from '@xapi-js/core';
|
|
6
|
+
|
|
7
|
+
describe('XapiRequestInterceptor', () => {
|
|
8
|
+
let interceptor: XapiRequestInterceptor;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
interceptor = new XapiRequestInterceptor();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should be defined', () => {
|
|
15
|
+
expect(interceptor).toBeDefined();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should deserialize Nexacro XAPI XML input to XapiRoot object', async () => {
|
|
19
|
+
const nexacroXmlInput = `<?xml version="1.0" encoding="UTF-8"?>
|
|
20
|
+
<Root xmlns="http://www.tobesoft.com/platform/Dataset" ver="4000">
|
|
21
|
+
<Dataset id="test">
|
|
22
|
+
<ColumnInfo>
|
|
23
|
+
<Column id="intCol" size="10" type="INT" />
|
|
24
|
+
<Column id="stringCol" size="50" type="STRING" />
|
|
25
|
+
</ColumnInfo>
|
|
26
|
+
<Rows>
|
|
27
|
+
<Row>
|
|
28
|
+
<Col id="intCol">123</Col>
|
|
29
|
+
<Col id="stringCol">hello</Col>
|
|
30
|
+
</Row>
|
|
31
|
+
<Row>
|
|
32
|
+
<Col id="intCol">456</Col>
|
|
33
|
+
<Col id="stringCol">world</Col>
|
|
34
|
+
</Row>
|
|
35
|
+
</Rows>
|
|
36
|
+
</Dataset>
|
|
37
|
+
</Root>`;
|
|
38
|
+
|
|
39
|
+
const mockRequest = {
|
|
40
|
+
body: nexacroXmlInput,
|
|
41
|
+
headers: { 'content-type': 'application/xml' },
|
|
42
|
+
};
|
|
43
|
+
const mockContext = {
|
|
44
|
+
switchToHttp: () => ({
|
|
45
|
+
getRequest: () => mockRequest,
|
|
46
|
+
getResponse: () => ({}),
|
|
47
|
+
}),
|
|
48
|
+
} as ExecutionContext;
|
|
49
|
+
|
|
50
|
+
const mockCallHandler = {
|
|
51
|
+
handle: () => of({}), // Mock a successful handler call
|
|
52
|
+
} as CallHandler;
|
|
53
|
+
|
|
54
|
+
const observableResult = await interceptor.intercept(mockContext, mockCallHandler);
|
|
55
|
+
await firstValueFrom(observableResult);
|
|
56
|
+
const request = mockContext.switchToHttp().getRequest();
|
|
57
|
+
|
|
58
|
+
expect(request.body).toBeInstanceOf(XapiRoot);
|
|
59
|
+
expect(request.body.datasets).toBeDefined();
|
|
60
|
+
expect(request.body.getDataset('test')).toBeDefined();
|
|
61
|
+
const dataset = request.body.getDataset('test');
|
|
62
|
+
expect(dataset.rows).toBeDefined();
|
|
63
|
+
expect(dataset.rows.length).toBe(2);
|
|
64
|
+
expect(dataset.getColumn(0, 'intCol')).toBe(123);
|
|
65
|
+
expect(dataset.getColumn(0, 'stringCol')).toBe('hello');
|
|
66
|
+
expect(dataset.getColumn(1, 'intCol')).toBe(456);
|
|
67
|
+
expect(dataset.getColumn(1, 'stringCol')).toBe('world');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should not deserialize non-XML input', async () => {
|
|
71
|
+
const jsonInput = `{ "data": "test" }`;
|
|
72
|
+
const mockRequest = {
|
|
73
|
+
body: jsonInput,
|
|
74
|
+
headers: { 'content-type': 'application/json' },
|
|
75
|
+
};
|
|
76
|
+
const mockContext = {
|
|
77
|
+
switchToHttp: () => ({
|
|
78
|
+
getRequest: () => mockRequest,
|
|
79
|
+
getResponse: () => ({}),
|
|
80
|
+
}),
|
|
81
|
+
} as ExecutionContext;
|
|
82
|
+
|
|
83
|
+
const mockCallHandler = {
|
|
84
|
+
handle: () => of({}),
|
|
85
|
+
} as CallHandler;
|
|
86
|
+
|
|
87
|
+
const observableResult = await interceptor.intercept(mockContext, mockCallHandler);
|
|
88
|
+
await firstValueFrom(observableResult);
|
|
89
|
+
const request = mockContext.switchToHttp().getRequest();
|
|
90
|
+
expect(request.body).toBe(jsonInput);
|
|
91
|
+
expect(request.body).not.toBeInstanceOf(XapiRoot);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should pass through if no body is present', async () => {
|
|
95
|
+
const mockRequest = {
|
|
96
|
+
headers: { 'content-type': 'application/xml' },
|
|
97
|
+
};
|
|
98
|
+
const mockContext = {
|
|
99
|
+
switchToHttp: () => ({
|
|
100
|
+
getRequest: () => mockRequest,
|
|
101
|
+
getResponse: () => ({}),
|
|
102
|
+
}),
|
|
103
|
+
} as ExecutionContext;
|
|
104
|
+
|
|
105
|
+
const mockCallHandler = {
|
|
106
|
+
handle: () => of({}),
|
|
107
|
+
} as CallHandler;
|
|
108
|
+
|
|
109
|
+
const observableResult = await interceptor.intercept(mockContext, mockCallHandler);
|
|
110
|
+
await firstValueFrom(observableResult);
|
|
111
|
+
const request = mockContext.switchToHttp().getRequest();
|
|
112
|
+
expect(request.body).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { of, firstValueFrom } from 'rxjs';
|
|
3
|
+
import { XapiResponseInterceptor } from '../src/xapi-response-interceptor';
|
|
4
|
+
import { CallHandler, ExecutionContext } from '@nestjs/common';
|
|
5
|
+
import { XapiRoot, Dataset, Column, writeString } from '@xapi-js/core';
|
|
6
|
+
|
|
7
|
+
// Mock the writeString function from @xapi-js/core
|
|
8
|
+
vi.mock('@xapi-js/core', async (importOriginal) => {
|
|
9
|
+
const actual = await importOriginal();
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
writeString: vi.fn(actual.writeString), // Use the actual implementation for mocking
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('XapiResponseInterceptor', () => {
|
|
17
|
+
let interceptor: XapiResponseInterceptor;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
interceptor = new XapiResponseInterceptor();
|
|
21
|
+
// Reset the mock before each test
|
|
22
|
+
vi.mocked(writeString).mockClear(); // Use writeString directly
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should be defined', () => {
|
|
26
|
+
expect(interceptor).toBeDefined();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should serialize XapiRoot output to Nexacro XAPI XML string', async () => {
|
|
30
|
+
const xapiRootOutput = new XapiRoot();
|
|
31
|
+
const dataset = new Dataset('test');
|
|
32
|
+
dataset.addColumn({ id: 'intCol', size: 10, type: 'INT' });
|
|
33
|
+
dataset.addColumn({ id: 'stringCol', size: 50, type: 'STRING' });
|
|
34
|
+
|
|
35
|
+
dataset.newRow();
|
|
36
|
+
dataset.setColumn(0, 'intCol', 123);
|
|
37
|
+
dataset.setColumn(0, 'stringCol', 'hello');
|
|
38
|
+
|
|
39
|
+
dataset.newRow();
|
|
40
|
+
dataset.setColumn(1, 'intCol', 456);
|
|
41
|
+
dataset.setColumn(1, 'stringCol', 'world');
|
|
42
|
+
|
|
43
|
+
xapiRootOutput.addDataset(dataset);
|
|
44
|
+
|
|
45
|
+
const mockContext = {
|
|
46
|
+
switchToHttp: () => ({
|
|
47
|
+
getRequest: () => ({}),
|
|
48
|
+
getResponse: () => ({}),
|
|
49
|
+
}),
|
|
50
|
+
} as ExecutionContext;
|
|
51
|
+
|
|
52
|
+
const mockCallHandler = {
|
|
53
|
+
handle: () => of(xapiRootOutput),
|
|
54
|
+
} as CallHandler;
|
|
55
|
+
|
|
56
|
+
const observableResult = interceptor.intercept(mockContext, mockCallHandler);
|
|
57
|
+
const result = await firstValueFrom(observableResult);
|
|
58
|
+
expect(typeof result).toBe('string');
|
|
59
|
+
expect(result).toContain('<Root xmlns="http://www.nexacroplatform.com/platform/dataset" version="4000">');
|
|
60
|
+
expect(result).toContain('<Dataset id="test">');
|
|
61
|
+
expect(result).toMatch(/<Column id="intCol" size="10" type="INT"\s*\/>/);
|
|
62
|
+
expect(result).toMatch(/<Column id="stringCol" size="50" type="STRING"\s*\/>/);
|
|
63
|
+
expect(result).toContain('<Rows>');
|
|
64
|
+
expect(result).toContain('<Row>');
|
|
65
|
+
expect(result).toContain('<Col id="intCol">123</Col>');
|
|
66
|
+
expect(result).toContain('<Col id="stringCol">hello</Col>');
|
|
67
|
+
expect(result).toContain('<Col id="intCol">456</Col>');
|
|
68
|
+
expect(result).toContain('<Col id="stringCol">world</Col>');
|
|
69
|
+
expect(result).toContain('</Row>');
|
|
70
|
+
expect(result).toContain('</Rows>');
|
|
71
|
+
expect(result).toContain('</Dataset>');
|
|
72
|
+
expect(result).toContain('</Root>');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should throw an error if handler does not return XapiRoot', async () => {
|
|
76
|
+
const nonXapiRootOutput = { message: 'hello' };
|
|
77
|
+
|
|
78
|
+
const mockContext = {
|
|
79
|
+
switchToHttp: () => ({
|
|
80
|
+
getRequest: () => ({}),
|
|
81
|
+
getResponse: () => ({}),
|
|
82
|
+
}),
|
|
83
|
+
} as ExecutionContext;
|
|
84
|
+
|
|
85
|
+
const mockCallHandler = {
|
|
86
|
+
handle: () => of(nonXapiRootOutput),
|
|
87
|
+
} as CallHandler;
|
|
88
|
+
|
|
89
|
+
const observableResult = interceptor.intercept(mockContext, mockCallHandler);
|
|
90
|
+
await expect(firstValueFrom(observableResult)).rejects.toThrow('Handler did not return an XapiRoot instance');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should re-throw error from writeString', async () => {
|
|
94
|
+
const xapiRootOutput = new XapiRoot();
|
|
95
|
+
|
|
96
|
+
const mockContext = {
|
|
97
|
+
switchToHttp: () => ({
|
|
98
|
+
getRequest: () => ({}),
|
|
99
|
+
getResponse: () => ({}),
|
|
100
|
+
}),
|
|
101
|
+
} as ExecutionContext;
|
|
102
|
+
|
|
103
|
+
const mockCallHandler = {
|
|
104
|
+
handle: () => of(xapiRootOutput),
|
|
105
|
+
} as CallHandler;
|
|
106
|
+
|
|
107
|
+
const testError = new Error('Test writeString error');
|
|
108
|
+
vi.mocked(writeString).mockRejectedValue(testError);
|
|
109
|
+
|
|
110
|
+
const observableResult = interceptor.intercept(mockContext, mockCallHandler);
|
|
111
|
+
await expect(firstValueFrom(observableResult)).rejects.toThrow(testError);
|
|
112
|
+
});
|
|
113
|
+
});
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
globals: true,
|
|
6
|
+
pool: "threads",
|
|
7
|
+
exclude: [
|
|
8
|
+
"**/node_modules/**",
|
|
9
|
+
"**/coverage/**",
|
|
10
|
+
"**/dist/**",
|
|
11
|
+
'**/.{idea,git,cache,output,temp}/**',
|
|
12
|
+
'**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*',
|
|
13
|
+
],
|
|
14
|
+
include: ['test/**/*.test.ts'],
|
|
15
|
+
},
|
|
16
|
+
})
|