Skip to content
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

Linking service #7

Merged
merged 7 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/build-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
on:
workflow_call:

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-push:
name: Build + Push Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- uses: docker/build-push-action@v5
with:
context: .
target: production
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
92 changes: 36 additions & 56 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,65 +1,45 @@
name: CI Pipeline

on:
push:
branches:
- '**'
push:
branches:
- '**'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: super-linter/super-linter@v5.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TYPESCRIPT_DEFAULT_STYLE: prettier
VALIDATE_DOCKERFILE_HADOLINT: false
VALIDATE_JSCPD: false
VALIDATE_PYTHON_FLAKE8: false
VALIDATE_PYTHON_MYPY: false
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: super-linter/super-linter@v5.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TYPESCRIPT_DEFAULT_STYLE: prettier
VALIDATE_DOCKERFILE_HADOLINT: false
VALIDATE_JSCPD: false
VALIDATE_PYTHON_FLAKE8: false
VALIDATE_PYTHON_MYPY: false

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 20
cache: 'npm'
- run: npm install --force
- run: npm run test
- uses: paambaati/codeclimate-action@v5.0.0
env:
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
with:
coverageCommand: npm run test:cov
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 20
cache: 'npm'
- run: npm install --force
- run: npm run test
- uses: paambaati/codeclimate-action@v5.0.0
env:
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
with:
coverageCommand: npm run test:cov

# build-push:
# name: Build and Push Image
# runs-on: ubuntu-latest
# needs: [lint, test]
# steps:
# - uses: actions/checkout@v4
# - uses: docker/setup-buildx-action@v3
# - uses: docker/login-action@v3
# with:
# registry: ${{ env.REGISTRY }}
# username: ${{ github.actor }}
# password: ${{ secrets.GITHUB_TOKEN }}
# - uses: docker/metadata-action@v5
# id: meta
# with:
# images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# - uses: docker/build-push-action@v5
# with:
# context: .
# target: prod
# push: true
# tags: ${{ steps.meta.outputs.tags }}
# labels: ${{ steps.meta.outputs.labels }}
build-push:
needs: [lint, test]
uses: ./.github/workflows/build-push.yml
36 changes: 12 additions & 24 deletions jest.config.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
{
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": ".",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverage": true,
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "./coverage",
"coverageReporters": [
"json",
"lcov",
"text",
"clover",
"html"
],
"testEnvironment": "node"
}
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverage": true,
"collectCoverageFrom": ["src/**/*.ts*"],
"coverageDirectory": "./coverage",
"coverageReporters": ["json", "lcov", "text", "clover", "html"],
"testEnvironment": "node"
}
212 changes: 212 additions & 0 deletions src/lit/lit.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import { Test, TestingModule } from '@nestjs/testing'
import { LitService } from './lit.service'
import { ConfigService } from '@nestjs/config'
import { Logger } from '@nestjs/common'
import {
LitNodeClientNodeJs,
encryptToJson,
} from '@lit-protocol/lit-node-client-nodejs'
import { keccak256, toHex, Address } from 'viem'
import { SupportedChainId } from '../shared/types/chain.type'

jest.mock('@lit-protocol/lit-node-client-nodejs', () => ({
LitNodeClientNodeJs: jest.fn().mockImplementation(() => ({
connect: jest.fn().mockResolvedValue(null),
disconnect: jest.fn().mockResolvedValue(null),
})),
encryptToJson: jest.fn().mockResolvedValue('encryptedData'),
}))

describe('LitService', () => {
let service: LitService
let litNodeClient: LitNodeClientNodeJs
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
LitService,
{
provide: ConfigService,
useValue: {
get: jest.fn().mockImplementation((key: string) => {
if (key === 'lit.network') {
return 'datil-dev'
}
return null
}),
},
},
{
provide: Logger,
useValue: {
error: jest.fn(),
},
},
],
}).compile()
service = module.get<LitService>(LitService)
litNodeClient = new LitNodeClientNodeJs({
alertWhenUnauthorized: false,
litNetwork: 'datil-dev',
debug: true,
})
service['litNodeClient'] = litNodeClient
})

it('should be defined', () => {
expect(service).toBeDefined()
})

// describe('connect', () => {
// it('should connect to the Lit node client', async () => {
// await service.connect()

// // Assert that the LitNodeClientNodeJs constructor was called with the expected config
// expect(litNodeClientConstructorSpy).toHaveBeenCalledWith({
// alertWhenUnauthorized: false,
// litNetwork: 'datil-dev',
// debug: true,
// })

// // Get the instance created by the constructor spy
// const litNodeClientInstance =
// litNodeClientConstructorSpy.mock.instances[0]

// // Ensure the connect method was called on this instance
// expect(litNodeClientInstance.connect).toHaveBeenCalled()
// })
// })

describe('disconnect', () => {
it('should disconnect from the Lit node client', async () => {
const litNodeClient = service['litNodeClient']
await service.disconnect()
expect(litNodeClient.disconnect).toHaveBeenCalled()
expect(service['litNodeClient']).toBeNull()
})
})

describe('getNetworkConfig', () => {
it('should return the correct network configuration', () => {
const mockNetworkConfig = { litNetwork: 'test-network' }
service['getNetworkConfig'] = jest
.fn()
.mockReturnValue(mockNetworkConfig)

expect(service.getNetworkConfig('test-network')).toEqual(
mockNetworkConfig
)
})
it('should throw an error if the network is unsupported', () => {
expect(() =>
service.getNetworkConfig('unsupported-network')
).toThrow(Error('Unsupported lit network: unsupported-network'))
})
})

describe('getContractAddress', () => {
it('should return the correct contract address for a supported chain ID', () => {
const mockChain = {
chainId: 1,
permissionManagerContractAddress: '0x123',
}
service['getContractAddress'] = jest
.fn()
.mockReturnValue(mockChain.permissionManagerContractAddress)

expect(service.getContractAddress(1 as SupportedChainId)).toBe(
'0x123'
)
})

it('should throw an error if the chain ID is unsupported', () => {
expect(() =>
service.getContractAbi(999 as SupportedChainId)
).toThrow(Error('Unsupported chain ID: 999'))
})
})

describe('generateEvmContractConditions', () => {
it('should generate the correct EVM contract conditions', () => {
const chainId = 1 as SupportedChainId
const userAddress: Address = '0xUser'
const mockConditions = [
{
contractAddress: '0x123',
functionName: 'testFunction',
functionParams: [
keccak256(toHex(userAddress)),
':userAddress',
],
functionAbi: {},
chain: 'ethereum',
returnValueTest: {
key: '',
comparator: '=',
value: 'true',
},
},
]

service['getContractAddress'] = jest.fn().mockReturnValue('0x123')
service['getContractFunctionName'] = jest
.fn()
.mockReturnValue('testFunction')
service['getContractAbi'] = jest.fn().mockReturnValue({})

const conditions = service.generateEvmContractConditions(
chainId,
userAddress
)
expect(conditions).toEqual(mockConditions)
})

it('should throw an error if the chain ID is unsupported', () => {
expect(() =>
service.getContractFunctionName(999 as SupportedChainId)
).toThrow(Error('Unsupported chain ID: 999'))
})
})

describe('encrypt', () => {
it('should successfully encrypt data', async () => {
const chainId = 1 as SupportedChainId
const dataToEncrypt = { test: 'data' }
const userAddress: Address = '0xUser'
const mockEncryptedData = 'encryptedData'

service['generateEvmContractConditions'] = jest
.fn()
.mockReturnValue([])
;(encryptToJson as jest.Mock).mockResolvedValue(mockEncryptedData)

const result = await service.encrypt(
chainId,
dataToEncrypt,
userAddress
)
expect(result).toBe(mockEncryptedData)
expect(encryptToJson).toHaveBeenCalledWith({
string: JSON.stringify(dataToEncrypt),
evmContractConditions: [],
litNodeClient: service['litNodeClient'],
chain: 'ethereum',
})
})
// it('should throw an InternalServerErrorException if encryption fails', async () => {
// const chainId = 1 as SupportedChainId
// const dataToEncrypt = { test: 'data' }
// const userAddress: Address = '0xUser'

// service['generateEvmContractConditions'] = jest
// .fn()
// .mockReturnValue([])
// ;(encryptToJson as jest.Mock).mockRejectedValue(
// new Error('Encryption failed')
// )

// await expect(
// service.encrypt(chainId, dataToEncrypt, userAddress)
// ).rejects.toThrow(InternalServerErrorException)
// })
})
})
2 changes: 1 addition & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Logger, LoggerErrorInterceptor } from 'nestjs-pino'
import * as session from 'express-session'
import { VersioningType, ValidationPipe } from '@nestjs/common'
import { setupSwagger } from './doc'
import { HttpExceptionFilter } from './filters/http-exception.filter'
import { HttpExceptionFilter } from './shared/filters/http-exception.filter'

async function bootstrap() {
const app = await NestFactory.create(AppModule, { bufferLogs: true })
Expand Down
Loading