Skip to content

Commit

Permalink
ci: test
Browse files Browse the repository at this point in the history
  • Loading branch information
nailiable committed Nov 1, 2024
1 parent 1f99166 commit 71e42b1
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 29 deletions.
52 changes: 52 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: CI

on:
push:
branches:
- v2
pull_request:
branches:
- v2

jobs:
test:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [18.x] # 使用 Node.js 18.x 版本

steps:
# Step 1: Checkout the repository
- name: Checkout code
uses: actions/checkout@v3

# Step 2: Set up Node.js
- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: pnpm # 缓存 pnpm 包

# Step 3: Install pnpm
- name: Install pnpm
run: |
npm install -g pnpm
# Step 4: Install dependencies (using pnpm)
- name: Install dependencies
run: |
pnpm install
# Step 5: Run tests for all packages
- name: Run tests
run: |
pnpm run test --filter
# (Optional) Step 6: Upload test results if needed
- name: Upload test results
if: always()
uses: actions/upload-artifact@v3
with:
name: test-results
path: ./packages/**/test-results.xml
9 changes: 9 additions & 0 deletions packages/ioc/src/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { IocPlugin } from './protocols'
import { FilterWatermark, InjectableWatermark, InjectWatermark, PostConstructWatermark } from './constant'
import { Container } from './container'
import { PluginRunner } from './plugin-runner'

Expand All @@ -14,5 +15,13 @@ export abstract class AbstractBootstrap extends Container {
return this
}

enableInternalConstant(): this {
this.createConstantWrapper(InjectableWatermark, InjectableWatermark).save()
this.createConstantWrapper(InjectWatermark, InjectWatermark).save()
this.createConstantWrapper(FilterWatermark, FilterWatermark).save()
this.createConstantWrapper(PostConstructWatermark, PostConstructWatermark).save()
return this
}

abstract run(...args: any[]): Promise<any>
}
14 changes: 14 additions & 0 deletions packages/ioc/src/container.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Saveable } from './protocols'
import type { Class, InjectionToken } from './types'
import { Injectable } from './decorators'
import { TaskRunner } from './task-runner'
import { ClassWrapper } from './wrappers/class-wrapper'
import { ConstantWrapper } from './wrappers/constant-wrapper'

Expand All @@ -12,6 +13,19 @@ export class Container implements Saveable {
return Container.map
}

private _taskRunner: TaskRunner | null = null
/**
* ### Get task runner.
*
* @description Get task runner for the class.
* @param cache - if false, it will create a new instance of task runner. Default is `true`.
*/
getTaskRunner(cache: boolean = true): TaskRunner {
if (this._taskRunner && cache) return this._taskRunner
this._taskRunner = new TaskRunner()
return this._taskRunner
}

replaceContainer(container: Map<InjectionToken, ClassWrapper | ConstantWrapper>): void {
Container.map = container
}
Expand Down
4 changes: 2 additions & 2 deletions packages/ioc/src/task-runner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export class TaskRunner {
async runTasksSequentially(tasks: Array<() => any>): Promise<void> {
async runTasksSequentially(tasks: Array<(...args: any[]) => any>): Promise<void> {
for (const task of tasks) {
try {
await task()
Expand All @@ -10,7 +10,7 @@ export class TaskRunner {
}
}

async runTasksInParallel(tasks: Array<() => any>): Promise<void> {
async runTasksInParallel(tasks: Array<(...args: any[]) => any>): Promise<void> {
const taskPromises = tasks.map(async (task) => {
try {
await task()
Expand Down
16 changes: 1 addition & 15 deletions packages/ioc/src/wrappers/class-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { Class, InjectionToken, PostConstructMetadata } from '../types'
import { PostConstructWatermark } from '../constant'
import { Container } from '../container'
import { MetadataScanner } from '../metadata-scanner'
import { TaskRunner } from '../task-runner'
import { InjectableFactory } from './injectable-factory'

export class ClassWrapper<Instance = any> implements ContainerWrapper {
Expand Down Expand Up @@ -41,19 +40,6 @@ export class ClassWrapper<Instance = any> implements ContainerWrapper {
return this._injectableFactory
}

private _taskRunner: TaskRunner | null = null
/**
* ### Get task runner.
*
* @description Get task runner for the class.
* @param cache - if false, it will create a new instance of task runner. Default is `true`.
*/
getTaskRunner(cache: boolean = true): TaskRunner {
if (this._taskRunner && cache) return this._taskRunner
this._taskRunner = new TaskRunner()
return this._taskRunner
}

getInjectionToken(): InjectionToken {
return this.getMetadataScanner()
.getInjectableMetadata()
Expand All @@ -63,7 +49,7 @@ export class ClassWrapper<Instance = any> implements ContainerWrapper {
private singletonInstance: null | Instance = null

setSingletonInstance(instance: Instance): void {
const taskRunner = this.getTaskRunner()
const taskRunner = this.getGlobalContainer().getTaskRunner()
const tasks: PostConstructMetadata[] = this.getMetadata(PostConstructWatermark) || []
// 这是全部一起开始执行的并行任务list
const parallelTasks = tasks.filter(({ callType }) => callType === 'parallel')
Expand Down
11 changes: 6 additions & 5 deletions packages/ioc/src/wrappers/injectable-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class InjectableFactory<Instance = any> implements ClassWrapperProvider {
return Reflect.construct(this.classWrapper.getTarget(), args)
}

getInjectedConstructorDependencies(): (ClassWrapper | ConstantWrapper | undefined)[] {
getInjectedConstructorDependencies(existDeps: (ClassWrapper | ConstantWrapper | undefined)[] = []): (ClassWrapper | ConstantWrapper | undefined)[] {
const dependencies: (ClassWrapper | ConstantWrapper | undefined)[] = []
const markedInjected: SingleInjectOptionWrapper[] = this.getMetadataScanner()
.getInjectMetadata()
Expand All @@ -38,14 +38,15 @@ export class InjectableFactory<Instance = any> implements ClassWrapperProvider {
const injectionToken = inject.getInjectionToken()
if (!injectionToken) continue
const wrapper = this.getGlobalContainer().getContainer().get(injectionToken)
if (!wrapper && inject.isRequired()) throw new Error(`Dependency not found for injectionToken ${injectionToken.toString()}.`)
if (!wrapper && inject.isRequired() && !existDeps[parameterIndex])
throw new Error(`Dependency not found for injectionToken ${injectionToken.toString()}.`)
dependencies[parameterIndex] = wrapper
}

return dependencies
}

getReflectConstructorDependencies(): (ClassWrapper | ConstantWrapper | undefined)[] {
getReflectConstructorDependencies(existDeps: (ClassWrapper | ConstantWrapper | undefined)[] = []): (ClassWrapper | ConstantWrapper | undefined)[] {
const dependencies: (ClassWrapper | ConstantWrapper | undefined)[] = []
const designParamTypes = this.getMetadataScanner().getConstructorParamTypes()
const markedInjected: SingleInjectOptionWrapper[] = this.getMetadataScanner()
Expand All @@ -58,7 +59,7 @@ export class InjectableFactory<Instance = any> implements ClassWrapperProvider {
if (designParamType === undefined) continue
const wrapper = this.getGlobalContainer().getContainer().get(designParamType)
const injectInfo = markedInjected.find(injectOptions => injectOptions.getParameterIndex() === i)
if (!wrapper && (!injectInfo || injectInfo.isRequired()))
if (!wrapper && (!injectInfo || injectInfo.isRequired()) && !existDeps[i])
throw new Error(`Dependency not found for designParamType ${designParamType.toString()}.`)
dependencies[i] = wrapper
}
Expand All @@ -68,7 +69,7 @@ export class InjectableFactory<Instance = any> implements ClassWrapperProvider {

getConstructorDependencies(): (ClassWrapper | ConstantWrapper | undefined)[] {
const injectedDependencies = this.getInjectedConstructorDependencies()
const reflectDependencies = this.getReflectConstructorDependencies()
const reflectDependencies = this.getReflectConstructorDependencies(injectedDependencies)
const dependencies: (ClassWrapper | ConstantWrapper | undefined)[] = []

// 如果 injectedDependencies 有值,就用 injectedDependencies,否则用 reflectDependencies
Expand Down
22 changes: 16 additions & 6 deletions packages/ioc/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Autowired, ClassWrapper, ConstantWrapper, Container, Inject, Injectable, Optional, PostConstruct } from '../src'
import { Autowired, ClassWrapper, ConstantWrapper, Container, Inject, Injectable, InjectableWatermark, InjectWatermark, Optional, PostConstruct } from '../src'
import { AbstractBootstrap } from '../src/bootstrap'

it('should automatic analyze deps', () => {
Expand All @@ -18,43 +18,53 @@ it('should automatic analyze deps', () => {
@Autowired()
baz2Service: BarService,
container: Container,
@Autowired(InjectableWatermark)
private readonly injectableWatermark: string,
) {
expect(barService).toBeInstanceOf(BarService)
expect(bazService).toBeInstanceOf(BarService)
expect(baz2Service).toBeInstanceOf(BarService)
expect(container).toBeInstanceOf(Container)
expect(injectableWatermark).toBe(InjectableWatermark)

// 属性注入的时候,在constructor 里 thisBarService 还没有被注入
expect(this.thisBarService).toBeUndefined()
expect(this.container).toBeUndefined()
expect(this.injectWatermark).toBeUndefined()
}

@Autowired()
thisBarService: Container
container: Container

@Autowired(InjectWatermark)
injectWatermark: string

@PostConstruct()
postConstructFunc() {
// 在 postConstruct 里 thisBarService 已经被注入
expect(this.thisBarService).toBeInstanceOf(Container)
expect(this.container).toBeInstanceOf(Container)
expect(this.injectWatermark).toBe(InjectWatermark)
}
}

class Bootstrap extends AbstractBootstrap {
async run(): Promise<any> {
this.enableInternalConstant()

const wrapper = this.createClassWrapper(FooService).save()
expect(wrapper.getMetadataScanner().isInjectable()).toBeTruthy()
expect(wrapper.getMetadataScanner().isFilter()).toBeFalsy()
const classFactory = wrapper.getClassFactory()
const constructorDeps = classFactory.getConstructorDependencies()
const propertyDeps = classFactory.getPropertyDependencies()
expect(propertyDeps.get('thisBarService')).toBeInstanceOf(ClassWrapper)
expect(propertyDeps.get('container')).toBeInstanceOf(ClassWrapper)
expect(constructorDeps[0]).toBeInstanceOf(ClassWrapper)
expect(constructorDeps[1]).toBeInstanceOf(ClassWrapper)
expect(constructorDeps[2]).toBeUndefined()
expect(constructorDeps[3]).toBeInstanceOf(ClassWrapper)

const instance: FooService = classFactory.getOrCreateInstance()
expect(instance).toBeInstanceOf(FooService)
expect(instance.thisBarService).toBeInstanceOf(Container)
expect(instance.container).toBeInstanceOf(Container)
}
}
new Bootstrap().run()
Expand Down
4 changes: 3 additions & 1 deletion vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { env } from 'node:process'
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: [],
},

ui: true,
reporters: env.GITHUB_ACTIONS ? ['dot', 'github-actions'] : ['dot'],
},
})

0 comments on commit 71e42b1

Please sign in to comment.