-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: fix aop in constructor inject type #247
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ import { EggPrototypeCrossCutHook } from '../src/EggPrototypeCrossCutHook'; | |
import { crossCutGraphHook } from '../src/CrossCutGraphHook'; | ||
import { pointCutGraphHook } from '../src/PointCutGraphHook'; | ||
import { CallTrace } from './fixtures/modules/hello_cross_cut/CallTrace'; | ||
import { HelloConstructorInject } from './fixtures/modules/constructor_inject_aop/Hello'; | ||
|
||
describe('test/aop-runtime.test.ts', () => { | ||
describe('succeed call', () => { | ||
|
@@ -160,4 +161,101 @@ describe('test/aop-runtime.test.ts', () => { | |
}, /Aop Advice\(PointcutAdvice\) not found in loadUnits/); | ||
}); | ||
}); | ||
|
||
describe('aop constructor should work', () => { | ||
let modules: Array<LoadUnitInstance>; | ||
let crosscutAdviceFactory: CrosscutAdviceFactory; | ||
let eggObjectAopHook: EggObjectAopHook; | ||
let loadUnitAopHook: LoadUnitAopHook; | ||
let eggPrototypeCrossCutHook: EggPrototypeCrossCutHook; | ||
|
||
beforeEach(async () => { | ||
crosscutAdviceFactory = new CrosscutAdviceFactory(); | ||
eggObjectAopHook = new EggObjectAopHook(); | ||
loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory); | ||
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory); | ||
EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook); | ||
LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook); | ||
EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook); | ||
|
||
modules = await CoreTestHelper.prepareModules([ | ||
path.join(__dirname, '..'), | ||
path.join(__dirname, 'fixtures/modules/constructor_inject_aop'), | ||
path.join(__dirname, 'fixtures/modules/hello_point_cut'), | ||
path.join(__dirname, 'fixtures/modules/hello_cross_cut'), | ||
], [ | ||
crossCutGraphHook, | ||
pointCutGraphHook, | ||
]); | ||
}); | ||
|
||
afterEach(async () => { | ||
for (const module of modules) { | ||
await LoadUnitFactory.destroyLoadUnit(module.loadUnit); | ||
await LoadUnitInstanceFactory.destroyLoadUnitInstance(module); | ||
} | ||
EggPrototypeLifecycleUtil.deleteLifecycle(eggPrototypeCrossCutHook); | ||
LoadUnitLifecycleUtil.deleteLifecycle(loadUnitAopHook); | ||
EggObjectLifecycleUtil.deleteLifecycle(eggObjectAopHook); | ||
}); | ||
Comment on lines
+165
to
+200
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Refactor to Eliminate Redundant The Suggested Refactor: Create helper functions for setup and teardown: // Define a type for the setup components
interface TestEnvironmentSetup {
modules: Array<LoadUnitInstance>;
crosscutAdviceFactory: CrosscutAdviceFactory;
eggObjectAopHook: EggObjectAopHook;
loadUnitAopHook: LoadUnitAopHook;
eggPrototypeCrossCutHook: EggPrototypeCrossCutHook;
}
// Extract setup logic
async function initializeTestEnvironment(modulePaths: string[]): Promise<TestEnvironmentSetup> {
const crosscutAdviceFactory = new CrosscutAdviceFactory();
const eggObjectAopHook = new EggObjectAopHook();
const loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory);
const eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory);
EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook);
LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook);
EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook);
const modules = await CoreTestHelper.prepareModules(modulePaths, [
crossCutGraphHook,
pointCutGraphHook,
]);
return {
modules,
crosscutAdviceFactory,
eggObjectAopHook,
loadUnitAopHook,
eggPrototypeCrossCutHook,
};
}
// Extract teardown logic
async function cleanupTestEnvironment(setup: TestEnvironmentSetup): Promise<void> {
for (const module of setup.modules) {
await LoadUnitFactory.destroyLoadUnit(module.loadUnit);
await LoadUnitInstanceFactory.destroyLoadUnitInstance(module);
}
EggPrototypeLifecycleUtil.deleteLifecycle(setup.eggPrototypeCrossCutHook);
LoadUnitLifecycleUtil.deleteLifecycle(setup.loadUnitAopHook);
EggObjectLifecycleUtil.deleteLifecycle(setup.eggObjectAopHook);
}
// Use the helper functions in your test suite
let testSetup: TestEnvironmentSetup;
beforeEach(async () => {
testSetup = await initializeTestEnvironment([
path.join(__dirname, '..'),
path.join(__dirname, 'fixtures/modules/constructor_inject_aop'),
path.join(__dirname, 'fixtures/modules/hello_point_cut'),
path.join(__dirname, 'fixtures/modules/hello_cross_cut'),
]);
});
afterEach(async () => {
await cleanupTestEnvironment(testSetup);
}); This refactor promotes code reuse and simplifies future maintenance. |
||
|
||
it('should work', async () => { | ||
await EggTestContext.mockContext(async () => { | ||
const hello = await CoreTestHelper.getObject(HelloConstructorInject); | ||
const callTrace = await CoreTestHelper.getObject(CallTrace); | ||
const msg = await hello.hello('aop'); | ||
const traceMsg = callTrace.msgs; | ||
console.log('msg: ', msg, traceMsg); | ||
assert.deepStrictEqual(msg, `withPointAroundResult(hello withPointAroundParam(aop)${JSON.stringify(pointcutAdviceParams)})`); | ||
assert.deepStrictEqual(traceMsg, [ | ||
{ | ||
className: 'PointcutAdvice', | ||
methodName: 'beforeCall', | ||
id: 233, | ||
name: 'aop', | ||
adviceParams: pointcutAdviceParams, | ||
}, | ||
{ | ||
className: 'PointcutAdvice', | ||
methodName: 'afterReturn', | ||
id: 233, | ||
name: 'withPointAroundParam(aop)', | ||
result: `withPointAroundResult(hello withPointAroundParam(aop)${JSON.stringify(pointcutAdviceParams)})`, | ||
adviceParams: pointcutAdviceParams, | ||
}, | ||
{ | ||
className: 'PointcutAdvice', | ||
methodName: 'afterFinally', | ||
id: 233, | ||
name: 'withPointAroundParam(aop)', | ||
adviceParams: pointcutAdviceParams, | ||
}, | ||
]); | ||
|
||
await assert.rejects(async () => { | ||
await hello.helloWithException('foo'); | ||
}, new Error('ops, exception for withPointAroundParam(foo)')); | ||
assert.deepStrictEqual(callTrace.msgs[callTrace.msgs.length - 2], { | ||
className: 'PointcutAdvice', | ||
methodName: 'afterThrow', | ||
id: 233, | ||
name: 'withPointAroundParam(foo)', | ||
result: 'ops, exception for withPointAroundParam(foo)', | ||
adviceParams: pointcutAdviceParams, | ||
}); | ||
}); | ||
}); | ||
|
||
it('mock should work', async () => { | ||
await EggTestContext.mockContext(async () => { | ||
const hello = await CoreTestHelper.getObject(HelloConstructorInject); | ||
let helloMocked = false; | ||
mm(HelloConstructorInject.prototype, 'hello', async () => { | ||
helloMocked = true; | ||
}); | ||
Comment on lines
+253
to
+255
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Avoid Mocking Prototypes Directly Mocking methods on the prototype directly using Suggested Modification: let helloMocked = false;
- mm(HelloConstructorInject.prototype, 'hello', async () => {
+ mm(hello, 'hello', async () => {
helloMocked = true;
}); By mocking the
|
||
await hello.hello('aop'); | ||
assert(helloMocked); | ||
}); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { ContextProto, Inject, SingletonProto } from '@eggjs/core-decorator'; | ||
import { Pointcut } from '@eggjs/aop-decorator'; | ||
import { PointcutAdvice, pointcutAdviceParams } from '../hello_point_cut/HelloPointCut'; | ||
|
||
@SingletonProto() | ||
export class Foo { | ||
} | ||
|
||
@ContextProto() | ||
export class HelloConstructorInject { | ||
id = 233; | ||
|
||
constructor(@Inject() readonly foo: Foo) { | ||
} | ||
|
||
@Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) | ||
async hello(name: string) { | ||
return `hello ${name}`; | ||
} | ||
|
||
@Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) | ||
async helloWithException(name: string) { | ||
throw new Error(`ops, exception for ${name}`); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"name": "aop-module", | ||
"eggModule": { | ||
"name": "aopModule" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,12 +35,12 @@ export class EggModuleLoader { | |
}); | ||
} | ||
const globalGraph = GlobalGraph.create(moduleDescriptors); | ||
globalGraph.build(); | ||
return globalGraph; | ||
} | ||
|
||
async load(): Promise<LoadUnit[]> { | ||
const loadUnits: LoadUnit[] = []; | ||
this.globalGraph.build(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Multiple The verification identified multiple instances of
Concerns:
Recommendations:
🔗 Analysis chainEnsure graph consistency by building before use. The addition of Consider the following points:
To ensure this change doesn't introduce any unintended side effects, please run the following verification: 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Check for any other occurrences of globalGraph.build() in the codebase
# Test: Search for other occurrences of globalGraph.build(). Expect: No other occurrences.
rg --type typescript 'globalGraph\.build\(\)'
# Test: Check if there are any error handling mechanisms for build(). Expect: Some form of error handling.
rg --type typescript 'globalGraph\.build\(\).*try|catch'
Length of output: 176 Script: #!/bin/bash
# Description: Check for any other occurrences of globalGraph.build() in the codebase
# Test: Search for other occurrences of globalGraph.build(). Expect: No other occurrences.
rg --type ts 'globalGraph\.build\(\)'
# Test: Check if there are any error handling mechanisms for build(). Expect: Some form of error handling.
rg --type ts 'globalGraph\.build\(\).*try|catch'
Length of output: 2409 |
||
this.globalGraph.sort(); | ||
const moduleConfigList = GlobalGraph.instance!.moduleConfigList; | ||
for (const moduleConfig of moduleConfigList) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Import Path Incorrect:
HelloConstructorInject
Not Exported from Specified ModuleThe module
'./fixtures/modules/constructor_inject_aop/Hello'
does not exportHelloConstructorInject
. Please verify thatHelloConstructorInject
is correctly exported incore/aop-runtime/test/fixtures/modules/constructor_inject_aop/Hello.ts
or update the import path to point to the correct module exportingHelloConstructorInject
.core/aop-runtime/test/fixtures/modules/constructor_inject_aop/Hello.ts
🔗 Analysis chain
Import Path Verification for
HelloConstructorInject
Please verify that the import path
'./fixtures/modules/constructor_inject_aop/Hello'
correctly leads to a module exportingHelloConstructorInject
. This ensures that the module is correctly resolved at runtime and prevents potential import errors.Run the following script to confirm the export:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 233
Script:
Length of output: 2768
Script:
Length of output: 155
Script:
Length of output: 176