mirror of
https://github.com/social-tw/social-tw-website.git
synced 2026-01-10 16:08:21 -05:00
Merge branch 'main' into fix/show-reputation-history-related-to-user
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react'
|
||||
import { wrapper } from '@/utils/test-helpers/wrapper'
|
||||
import { renderHook, waitFor } from '@testing-library/react'
|
||||
import { useEpoch } from './useEpoch'
|
||||
|
||||
// Mock useUserState
|
||||
jest.mock('@/features/core/hooks/useUserState/useUserState', () => ({
|
||||
useUserState: () => ({
|
||||
userState: {
|
||||
@@ -13,10 +14,22 @@ jest.mock('@/features/core/hooks/useUserState/useUserState', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock useRelayConfig
|
||||
jest.mock('@/features/core/hooks/useRelayConfig/useRelayConfig', () => ({
|
||||
useRelayConfig: () => ({
|
||||
data: {
|
||||
EPOCH_LENGTH: 300, // 300 seconds
|
||||
},
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
}),
|
||||
}))
|
||||
|
||||
describe('useEpoch', () => {
|
||||
it('should get epoch time information', async () => {
|
||||
const { result } = renderHook(useEpoch, { wrapper })
|
||||
|
||||
await waitFor(() => expect(result.current.isPending).toBe(false))
|
||||
await waitFor(() => expect(result.current.currentEpoch).toBe(2))
|
||||
await waitFor(() => expect(result.current.remainingTime).toBe(120000))
|
||||
await waitFor(() => expect(result.current.epochLength).toBe(300000))
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { QueryKeys } from '@/constants/queryKeys'
|
||||
import { useUserState } from '@/features/core'
|
||||
import { useRelayConfig, useUserState } from '@/features/core'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import isNull from 'lodash/isNull'
|
||||
import isUndefined from 'lodash/isUndefined'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
|
||||
const epochLength = 300000 // 300000 ms
|
||||
|
||||
export function useEpoch() {
|
||||
const { isPending: isUserStatePending, userState } = useUserState()
|
||||
const { data: config, isPending: isConfigPending } = useRelayConfig()
|
||||
|
||||
// Convert EPOCH_LENGTH from seconds to milliseconds
|
||||
const epochLength = useMemo(() => {
|
||||
return config?.EPOCH_LENGTH ? config.EPOCH_LENGTH * 1000 : undefined
|
||||
}, [config])
|
||||
|
||||
const {
|
||||
isPending: isCurrentEpochPending,
|
||||
@@ -23,6 +27,7 @@ export function useEpoch() {
|
||||
return userState.sync.calcCurrentEpoch()
|
||||
},
|
||||
staleTime: epochLength,
|
||||
enabled: !!epochLength,
|
||||
})
|
||||
|
||||
const {
|
||||
@@ -39,21 +44,27 @@ export function useEpoch() {
|
||||
return time * 1000
|
||||
},
|
||||
staleTime: epochLength,
|
||||
enabled: !!epochLength,
|
||||
})
|
||||
|
||||
const isPending =
|
||||
isUserStatePending || isCurrentEpochPending || isRemainingTimePending
|
||||
isUserStatePending ||
|
||||
isCurrentEpochPending ||
|
||||
isRemainingTimePending ||
|
||||
isConfigPending ||
|
||||
!epochLength
|
||||
|
||||
const epochStartTime = useMemo(() => {
|
||||
if (
|
||||
isUndefined(currentEpoch) ||
|
||||
isNull(currentEpoch) ||
|
||||
!remainingTime
|
||||
!remainingTime ||
|
||||
!epochLength
|
||||
) {
|
||||
return 0
|
||||
}
|
||||
return Date.now() - (epochLength - remainingTime)
|
||||
}, [currentEpoch, remainingTime])
|
||||
}, [currentEpoch, remainingTime, epochLength])
|
||||
|
||||
const epochEndTime = useMemo(() => {
|
||||
if (
|
||||
@@ -70,7 +81,8 @@ export function useEpoch() {
|
||||
if (
|
||||
isUndefined(currentEpoch) ||
|
||||
isNull(currentEpoch) ||
|
||||
!remainingTime
|
||||
!remainingTime ||
|
||||
!epochLength
|
||||
) {
|
||||
return
|
||||
}
|
||||
@@ -85,7 +97,13 @@ export function useEpoch() {
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}, [currentEpoch, refetchCurrentEpoch, refetchRemainingTime, remainingTime])
|
||||
}, [
|
||||
currentEpoch,
|
||||
refetchCurrentEpoch,
|
||||
refetchRemainingTime,
|
||||
remainingTime,
|
||||
epochLength,
|
||||
])
|
||||
|
||||
return {
|
||||
isPending,
|
||||
|
||||
@@ -5,6 +5,16 @@ import { act, renderHook } from '@testing-library/react'
|
||||
import nock from 'nock'
|
||||
import { useCreatePost } from './useCreatePost'
|
||||
|
||||
jest.mock('@/features/core/hooks/useRelayConfig/useRelayConfig', () => ({
|
||||
useRelayConfig: () => ({
|
||||
data: {
|
||||
EPOCH_LENGTH: 300,
|
||||
},
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
}),
|
||||
}))
|
||||
|
||||
jest.mock('@/features/core/hooks/useWeb3Provider/useWeb3Provider', () => ({
|
||||
useWeb3Provider: () => ({
|
||||
getGuaranteedProvider: () => ({
|
||||
|
||||
@@ -5,6 +5,16 @@ import { act, renderHook } from '@testing-library/react'
|
||||
import nock from 'nock'
|
||||
import { useVotes } from './useVotes'
|
||||
|
||||
jest.mock('@/features/core/hooks/useRelayConfig/useRelayConfig', () => ({
|
||||
useRelayConfig: () => ({
|
||||
data: {
|
||||
EPOCH_LENGTH: 300,
|
||||
},
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
}),
|
||||
}))
|
||||
|
||||
jest.mock('@/features/core/hooks/useUserState/useUserState', () => ({
|
||||
useUserState: () => ({
|
||||
userState: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useUserState } from '@/features/core'
|
||||
import { useRelayConfig, useUserState } from '@/features/core'
|
||||
import dayjs from 'dayjs'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import {
|
||||
EpochDateService,
|
||||
FromToEpoch,
|
||||
@@ -9,14 +9,20 @@ import {
|
||||
|
||||
export function useDatePicker() {
|
||||
const { userState } = useUserState()
|
||||
const { data: config } = useRelayConfig()
|
||||
const [startDate, setStartDate] = useState<undefined | Date>(undefined)
|
||||
const [endDate, setEndDate] = useState<undefined | Date>(undefined)
|
||||
const [fromToEpoch, setFromToEpoch] = useState<FromToEpoch>(
|
||||
new InvalidFromToEpoch(),
|
||||
)
|
||||
|
||||
// Calculate epochLength in milliseconds
|
||||
const epochLength = useMemo(() => {
|
||||
return config?.EPOCH_LENGTH ? config.EPOCH_LENGTH * 1000 : undefined
|
||||
}, [config])
|
||||
|
||||
const updateFromToEpoch = useCallback(async () => {
|
||||
if (!userState) {
|
||||
if (!userState || !epochLength) {
|
||||
setFromToEpoch(new InvalidFromToEpoch())
|
||||
return
|
||||
}
|
||||
@@ -25,9 +31,10 @@ export function useDatePicker() {
|
||||
startDate,
|
||||
endDate,
|
||||
userState.sync,
|
||||
epochLength,
|
||||
),
|
||||
)
|
||||
}, [startDate, endDate, userState])
|
||||
}, [startDate, endDate, userState, epochLength])
|
||||
|
||||
const onChange = (dates: [Date | null, Date | null]) => {
|
||||
const [start, end] = dates
|
||||
|
||||
@@ -15,6 +15,7 @@ describe('EpochDateService', () => {
|
||||
it('should execute properly', () => {
|
||||
const synchronizer =
|
||||
new MockSynchronizer() as unknown as Synchronizer
|
||||
const epochLength = 300000 // 5 minutes in milliseconds
|
||||
|
||||
// service start time: 1720656000000 = 1721088000000 - 0 - 300000 * 1440
|
||||
// service start time: 2024-07-11T00:00:00.000Z
|
||||
@@ -23,25 +24,45 @@ describe('EpochDateService', () => {
|
||||
// at service start & now
|
||||
const date0 = new Date('2024-07-11T00:00:00Z')
|
||||
expect(
|
||||
EpochDateService.calcEpochByDate(mockNow, date0, synchronizer),
|
||||
EpochDateService.calcEpochByDate(
|
||||
mockNow,
|
||||
date0,
|
||||
synchronizer,
|
||||
epochLength,
|
||||
),
|
||||
).toBe(0)
|
||||
|
||||
// between service start & now
|
||||
const date1 = new Date('2024-07-11T00:15:00Z')
|
||||
expect(
|
||||
EpochDateService.calcEpochByDate(mockNow, date1, synchronizer),
|
||||
EpochDateService.calcEpochByDate(
|
||||
mockNow,
|
||||
date1,
|
||||
synchronizer,
|
||||
epochLength,
|
||||
),
|
||||
).toBe(3) // 724 * 300000 + 1720655850000
|
||||
|
||||
// before service start
|
||||
const date2 = new Date('2024-07-10T23:59:00Z')
|
||||
expect(
|
||||
EpochDateService.calcEpochByDate(mockNow, date2, synchronizer),
|
||||
EpochDateService.calcEpochByDate(
|
||||
mockNow,
|
||||
date2,
|
||||
synchronizer,
|
||||
epochLength,
|
||||
),
|
||||
).toBe(0)
|
||||
|
||||
// future
|
||||
const date3 = new Date('2024-07-17T00:00:00Z')
|
||||
expect(
|
||||
EpochDateService.calcEpochByDate(mockNow, date3, synchronizer),
|
||||
EpochDateService.calcEpochByDate(
|
||||
mockNow,
|
||||
date3,
|
||||
synchronizer,
|
||||
epochLength,
|
||||
),
|
||||
).toBe(1440 + 12 * 24)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,21 +9,32 @@ export class EpochDateService {
|
||||
start: Date | undefined,
|
||||
end: Date | undefined,
|
||||
synchoronizer: Synchronizer,
|
||||
epochLength: number,
|
||||
) {
|
||||
if (!!start && !!end && EpochDateService.isValidDateRange(start, end)) {
|
||||
const [from, to] = EpochDateService.calcEpochsByDates(
|
||||
[start, end],
|
||||
synchoronizer,
|
||||
epochLength,
|
||||
)
|
||||
return new ValidFromToEpoch(from, to)
|
||||
}
|
||||
return new InvalidFromToEpoch()
|
||||
}
|
||||
|
||||
static calcEpochsByDates(dates: Date[], synchoronizer: Synchronizer) {
|
||||
static calcEpochsByDates(
|
||||
dates: Date[],
|
||||
synchoronizer: Synchronizer,
|
||||
epochLength: number,
|
||||
) {
|
||||
const now = Date.now()
|
||||
return dates.map((date) =>
|
||||
EpochDateService.calcEpochByDate(now, date, synchoronizer),
|
||||
EpochDateService.calcEpochByDate(
|
||||
now,
|
||||
date,
|
||||
synchoronizer,
|
||||
epochLength,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,8 +42,8 @@ export class EpochDateService {
|
||||
now: number,
|
||||
date: Date,
|
||||
synchoronizer: Synchronizer,
|
||||
epochLength: number,
|
||||
) {
|
||||
const epochLength = 300000
|
||||
const currentEpoch = synchoronizer.calcCurrentEpoch()
|
||||
const remainingTime = synchoronizer.calcEpochRemainingTime() * 1000
|
||||
const currentEpochStartTime = now - (epochLength - remainingTime)
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface FetchRelayConfigResponse {
|
||||
UNIREP_ADDRESS: string
|
||||
APP_ADDRESS: string
|
||||
ETH_PROVIDER_URL: string
|
||||
EPOCH_LENGTH: number
|
||||
}
|
||||
|
||||
export type FetchPostsResponse = RelayRawPost[]
|
||||
|
||||
@@ -6,6 +6,7 @@ export function buildMockConfigAPI() {
|
||||
UNIREP_ADDRESS: '0x83cB6AF63eAfEc7998cC601eC3f56d064892b386',
|
||||
APP_ADDRESS: '0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1',
|
||||
ETH_PROVIDER_URL: 'http://127.0.0.1:8545',
|
||||
EPOCH_LENGTH: 300,
|
||||
}
|
||||
const expectation = nock(SERVER).get('/api/config').reply(200, response)
|
||||
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
import { DB } from 'anondb/node'
|
||||
import { Express } from 'express'
|
||||
import { UNIREP_ADDRESS, APP_ADDRESS, ETH_PROVIDER_URL } from '../config'
|
||||
import { APP_ADDRESS, ETH_PROVIDER_URL, UNIREP_ADDRESS } from '../config'
|
||||
import { UnirepSocialSynchronizer } from '../services/singletons/UnirepSocialSynchronizer'
|
||||
|
||||
export default (
|
||||
app: Express,
|
||||
_: DB,
|
||||
synchronizer: UnirepSocialSynchronizer
|
||||
) => {
|
||||
app.get('/api/config', async (_, res) => {
|
||||
const epochLength =
|
||||
await synchronizer.unirepContract.attesterEpochLength(
|
||||
BigInt(APP_ADDRESS).toString()
|
||||
)
|
||||
|
||||
export default (app: Express) => {
|
||||
app.get('/api/config', (_, res) =>
|
||||
res.json({
|
||||
UNIREP_ADDRESS,
|
||||
APP_ADDRESS,
|
||||
ETH_PROVIDER_URL,
|
||||
EPOCH_LENGTH: epochLength,
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
77
packages/relay/test/config.test.ts
Normal file
77
packages/relay/test/config.test.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { Unirep, UnirepApp } from '@unirep-app/contracts/typechain-types'
|
||||
import { DB } from 'anondb'
|
||||
import { expect } from 'chai'
|
||||
import { ethers } from 'hardhat'
|
||||
import { ETH_PROVIDER_URL } from '../src/config'
|
||||
import { UnirepSocialSynchronizer } from '../src/services/singletons/UnirepSocialSynchronizer'
|
||||
import { deployContracts, startServer, stopServer } from './environment'
|
||||
|
||||
describe('GET /api/config', function () {
|
||||
let snapshot: any
|
||||
let express: ChaiHttp.Agent
|
||||
let unirep: Unirep
|
||||
let app: UnirepApp
|
||||
let db: DB
|
||||
let prover: any
|
||||
let provider: any
|
||||
let sync: UnirepSocialSynchronizer
|
||||
|
||||
before(async function () {
|
||||
snapshot = await ethers.provider.send('evm_snapshot', [])
|
||||
// deploy contracts
|
||||
const { unirep: _unirep, app: _app } = await deployContracts(100000)
|
||||
// start server
|
||||
const {
|
||||
db: _db,
|
||||
prover: _prover,
|
||||
provider: _provider,
|
||||
synchronizer,
|
||||
chaiServer,
|
||||
} = await startServer(_unirep, _app)
|
||||
express = chaiServer
|
||||
unirep = _unirep
|
||||
db = _db
|
||||
app = _app
|
||||
prover = _prover
|
||||
provider = _provider
|
||||
sync = synchronizer
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
await stopServer('config', snapshot, sync, express)
|
||||
})
|
||||
|
||||
it('should return the correct configuration', async function () {
|
||||
const res = await express
|
||||
.get('/api/config')
|
||||
.set('content-type', 'application/json')
|
||||
|
||||
expect(res).to.have.status(200)
|
||||
expect(res.body).to.have.property('UNIREP_ADDRESS')
|
||||
expect(res.body).to.have.property('APP_ADDRESS')
|
||||
expect(res.body).to.have.property('ETH_PROVIDER_URL')
|
||||
expect(res.body).to.have.property('EPOCH_LENGTH')
|
||||
|
||||
expect(res.body.UNIREP_ADDRESS).to.equal(unirep.address)
|
||||
expect(res.body.APP_ADDRESS).to.equal(app.address)
|
||||
expect(res.body.ETH_PROVIDER_URL).to.equal(ETH_PROVIDER_URL)
|
||||
|
||||
const expectedEpochLength =
|
||||
await sync.unirepContract.attesterEpochLength(
|
||||
BigInt(app.address).toString()
|
||||
)
|
||||
expect(res.body.EPOCH_LENGTH).to.equal(expectedEpochLength)
|
||||
})
|
||||
|
||||
it('should return the correct data types', async function () {
|
||||
const res = await express
|
||||
.get('/api/config')
|
||||
.set('content-type', 'application/json')
|
||||
|
||||
expect(res).to.have.status(200)
|
||||
expect(res.body.UNIREP_ADDRESS).to.be.a('string')
|
||||
expect(res.body.APP_ADDRESS).to.be.a('string')
|
||||
expect(res.body.ETH_PROVIDER_URL).to.be.a('string')
|
||||
expect(res.body.EPOCH_LENGTH).to.be.a('number')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user