fet: Add changes lol
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
This commit is contained in:
parent
9530be5c43
commit
ff07f8f4a5
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"editor.quickSuggestions": {
|
||||
"strings": "on"
|
||||
},
|
||||
"tailwindCSS.includeLanguages": {
|
||||
"astro": "html"
|
||||
}
|
||||
}
|
@ -2,4 +2,4 @@ it('titles are correct', () => {
|
||||
const page = cy.visit('http://localhost:3000');
|
||||
|
||||
page.get('h1').should('have.text', 'Welcome to Astro');
|
||||
});
|
||||
})
|
||||
|
757
package-lock.json
generated
757
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
@ -12,25 +12,26 @@
|
||||
"test": "vitest --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^5.2.0",
|
||||
"@astrojs/tailwind": "^3.1.3",
|
||||
"@dzeio/logger": "^3.0.0",
|
||||
"@dzeio/object-util": "^1.5.0",
|
||||
"@dzeio/url-manager": "^1.0.10",
|
||||
"astro": "^2.6.4",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"mathjs": "^11.8.1",
|
||||
"mongoose": "^7.3.0",
|
||||
"tailwindcss": "^3.3.2"
|
||||
"@astrojs/node": "^5",
|
||||
"@astrojs/tailwind": "^3",
|
||||
"@dzeio/logger": "^3",
|
||||
"@dzeio/object-util": "^1",
|
||||
"@dzeio/url-manager": "^1",
|
||||
"astro": "^2",
|
||||
"bcryptjs": "^2",
|
||||
"jsonwebtoken": "^9",
|
||||
"lucide-astro": "^0.256.1",
|
||||
"mathjs": "^11",
|
||||
"mongoose": "^7",
|
||||
"tailwindcss": "^3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/bcryptjs": "^2",
|
||||
"@types/jsonwebtoken": "^9",
|
||||
"@types/node": "^20",
|
||||
"@vitest/coverage-v8": "^0.32.2",
|
||||
"cypress": "^12.15.0",
|
||||
"cypress": "^12",
|
||||
"vitest": "^0.32.2"
|
||||
}
|
||||
}
|
||||
|
5
public/logo.svg
Normal file
5
public/logo.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="206" height="110" viewBox="0 0 206 110" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M63.7908 75.4986V37.0836H89.5535V44.3452H72.4524V52.383H87.6657V59.6447H72.4524V75.4986H63.7908ZM95.6732 37.0836H104.335V75.4986H95.6732V37.0836ZM121.459 76.3301C120.459 76.3301 119.404 76.2564 118.294 76.1083C117.183 75.9975 116.11 75.8312 115.073 75.6094C114.037 75.3877 113.093 75.1478 112.242 74.8888C111.39 74.6303 110.724 74.3899 110.243 74.1682L111.853 67.2945C112.815 67.7011 114.037 68.1446 115.518 68.6249C117.035 69.0683 118.904 69.2901 121.125 69.2901C123.679 69.2901 125.549 68.8098 126.733 67.8488C127.917 66.8883 128.51 65.5947 128.51 63.9685C128.51 62.9707 128.288 62.1392 127.844 61.474C127.436 60.772 126.863 60.2177 126.122 59.811C125.382 59.3675 124.494 59.0722 123.457 58.9241C122.458 58.7396 121.384 58.6469 120.237 58.6469H117.017V51.9949H120.681C121.495 51.9949 122.273 51.9213 123.013 51.7732C123.791 51.6255 124.475 51.3852 125.068 51.0526C125.66 50.6832 126.122 50.2029 126.456 49.6113C126.826 48.9834 127.011 48.2073 127.011 47.2831C127.011 46.5811 126.863 45.9714 126.567 45.4538C126.27 44.9368 125.882 44.5115 125.401 44.1789C124.956 43.8463 124.419 43.6064 123.791 43.4583C123.198 43.2736 122.587 43.1812 121.958 43.1812C120.366 43.1812 118.886 43.4214 117.516 43.9017C116.184 44.3824 114.962 44.9736 113.852 45.6756L110.909 39.6335C111.501 39.264 112.186 38.8759 112.963 38.4694C113.778 38.0629 114.666 37.6933 115.629 37.3608C116.591 37.0282 117.609 36.751 118.682 36.5293C119.793 36.3075 120.959 36.1967 122.18 36.1967C124.438 36.1967 126.381 36.4738 128.01 37.0282C129.676 37.5455 131.045 38.3031 132.119 39.3009C133.192 40.2618 133.988 41.4074 134.506 42.7378C135.024 44.0312 135.284 45.4538 135.284 47.006C135.284 48.5213 134.858 49.9994 134.007 51.4406C133.155 52.8451 132.008 53.9169 130.564 54.6557C132.563 55.469 134.099 56.6886 135.173 58.3143C136.283 59.9037 136.838 61.8252 136.838 64.0794C136.838 65.8532 136.542 67.498 135.95 69.0129C135.358 70.4914 134.432 71.7846 133.174 72.8932C131.915 73.9651 130.305 74.8152 128.343 75.4431C126.418 76.0347 124.124 76.3301 121.459 76.3301ZM153.46 68.4586C153.867 68.4958 154.33 68.5326 154.848 68.5694C155.403 68.5694 156.051 68.5694 156.791 68.5694C161.122 68.5694 164.324 67.4794 166.397 65.2989C168.507 63.1188 169.562 60.1068 169.562 56.2633C169.562 52.2353 168.562 49.1865 166.563 47.1168C164.564 45.0476 161.4 44.0126 157.069 44.0126C156.476 44.0126 155.866 44.0312 155.237 44.068C154.607 44.068 154.015 44.1053 153.46 44.1789V68.4586ZM178.501 56.2633C178.501 59.5893 177.982 62.4904 176.946 64.9663C175.909 67.4426 174.429 69.4936 172.504 71.1194C170.616 72.7455 168.303 73.9651 165.564 74.7779C162.825 75.5912 159.752 75.9975 156.347 75.9975C154.792 75.9975 152.978 75.9239 150.906 75.7757C148.833 75.6649 146.797 75.4063 144.798 74.9997V37.5825C146.797 37.2129 148.87 36.9727 151.017 36.8619C153.2 36.714 155.07 36.6401 156.625 36.6401C159.919 36.6401 162.899 37.0097 165.564 37.7488C168.266 38.4879 170.579 39.652 172.504 41.2411C174.429 42.8302 175.909 44.8627 176.946 47.3386C177.982 49.8149 178.501 52.7896 178.501 56.2633Z" fill="black"/>
|
||||
<path d="M49.5 15C48.806 14.3071 48.0241 14 47 14H18C16.9758 14 16.1917 14.3071 15.5 15C14.806 15.6905 14.5 16.4775 14.5 17.5V75.5C14.5 76.5225 14.806 77.3071 15.5 78C16.1917 78.6908 16.9758 79 18 79H25.5V82.5C25.5 83.5225 25.8083 84.3071 26.5 85C27.194 85.6903 27.9758 86 29 86H36C37.0242 86 37.8084 85.6903 38.5 85C39.194 84.3071 39.5 83.5225 39.5 82.5V79H47C48.0241 79 48.806 78.6908 49.5 78C50.1914 77.3071 50.5 76.5225 50.5 75.5V17.5C50.5 16.4775 50.1914 15.6905 49.5 15Z" fill="black"/>
|
||||
<path d="M189.53 96H33C30.5 96 29.5 95 29.5 92.5V86H35.5V88C35.5 90 36.5 91 38.5 91H189.53C190.5 91 191.5 91.5 191.5 93.5C191.5 95.5 190.5 96 189.53 96Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
@ -1,11 +0,0 @@
|
||||
{
|
||||
"infiniteLoopProtection": true,
|
||||
"hardReloadOnChange": false,
|
||||
"view": "browser",
|
||||
"template": "node",
|
||||
"container": {
|
||||
"port": 3000,
|
||||
"startScript": "start",
|
||||
"node": "14"
|
||||
}
|
||||
}
|
2
src/env.d.ts
vendored
2
src/env.d.ts
vendored
@ -22,7 +22,7 @@ declare namespace App {
|
||||
authKey?: string
|
||||
responseBuilder: {
|
||||
body(body: string | Buffer | object | null | undefined): this
|
||||
headers(headers: HeadersInit ): this
|
||||
headers(headers: Record<string, string>): this
|
||||
addHeader(key: string, value: string): this
|
||||
addHeaders(headers: Record<string, string>): this
|
||||
removeHeader(key: string): this
|
||||
|
@ -1,4 +1,5 @@
|
||||
---
|
||||
import { WifiOff } from 'lucide-astro'
|
||||
export interface Props {
|
||||
title: string;
|
||||
}
|
||||
@ -16,21 +17,21 @@ const { title } = Astro.props;
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<body class="bg-gray-50">
|
||||
|
||||
<aside class="fixed top-0 left-0 z-40 w-64 p-4 h-screen bg-white border-r-2 border-gray-100">
|
||||
<div class="mb-2 flex">
|
||||
<img src="/logo.svg" class="w-1/2" alt="FI3D Logo">
|
||||
<WifiOff class='pokemon' />
|
||||
</div>
|
||||
<ul class="space-y-2 font-medium">
|
||||
<li class="p-4 w-full bg-red-100 hover:bg-red-200 cursor-pointer text-center">Item</li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<div class="p-4 sm:ml-64">
|
||||
<slot />
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<style is:global>
|
||||
:root {
|
||||
--accent: 124, 58, 237;
|
||||
--accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%);
|
||||
}
|
||||
html {
|
||||
font-family: system-ui, sans-serif;
|
||||
background-color: #F6F6F6;
|
||||
}
|
||||
code {
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
</style>
|
||||
|
3
src/libs/README.md
Normal file
3
src/libs/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Libs
|
||||
|
||||
Globally independent objects/classes/functions that MUST be unit testable by themselve
|
@ -1,6 +1,6 @@
|
||||
import { objectLoop } from '@dzeio/object-util'
|
||||
import StatusCode from './HTTP/StatusCode'
|
||||
import { buildRFC7807 } from './RFCs/RFC7807'
|
||||
import StatusCode from './StatusCode'
|
||||
import ResponseBuilder from './ResponseBuilder'
|
||||
|
||||
interface StorageItem {
|
||||
pointsRemaining: number
|
||||
@ -33,36 +33,40 @@ export default class RateLimiter {
|
||||
|
||||
private storage: Record<string, StorageItem> = {}
|
||||
|
||||
public constructor(
|
||||
private points = RateLimiter.points,
|
||||
private timeSpan = RateLimiter.timeSpan
|
||||
) {}
|
||||
|
||||
public consume(key: string, value: number = 1): Response | RateLimitHeaders {
|
||||
let item = this.storage[key]
|
||||
const now = (new Date().getTime() / 1000)
|
||||
if (!item) {
|
||||
item = {
|
||||
pointsRemaining: RateLimiter.points,
|
||||
timeReset: now + RateLimiter.timeSpan
|
||||
pointsRemaining: this.points,
|
||||
timeReset: now + this.timeSpan
|
||||
}
|
||||
}
|
||||
if (item.timeReset <= now) {
|
||||
item.timeReset = now + RateLimiter.timeSpan
|
||||
item.pointsRemaining = RateLimiter.points
|
||||
item.timeReset = now + this.timeSpan
|
||||
item.pointsRemaining = this.points
|
||||
}
|
||||
item.pointsRemaining -= value
|
||||
this.storage[key] = item
|
||||
const headers: RateLimitHeaders = {
|
||||
"X-RateLimit-Limit": RateLimiter.points.toFixed(0),
|
||||
"X-RateLimit-Limit": this.points.toFixed(0),
|
||||
"X-RateLimit-Remaining": Math.max(item.pointsRemaining, 0).toFixed(0),
|
||||
"X-RateLimit-Reset": item.timeReset.toFixed(0)
|
||||
}
|
||||
if (item.pointsRemaining < 0) {
|
||||
const res = new ResponseBuilder()
|
||||
const resp = buildRFC7807({
|
||||
type: '/docs/error/rate-limited',
|
||||
status: StatusCode.TOO_MANY_REQUESTS,
|
||||
title: 'You are being rate limited as you have done too many requests to the server'
|
||||
})
|
||||
resp.headers.append('Retry-After', (item.timeReset - now).toFixed(0))
|
||||
objectLoop(headers, (value, key) => {
|
||||
resp.headers.append(key, value)
|
||||
})
|
||||
}, res)
|
||||
res.addHeader('Retry-After', (item.timeReset - now).toFixed(0))
|
||||
res.addHeaders(headers as any)
|
||||
return resp
|
||||
}
|
||||
return headers
|
||||
|
@ -18,18 +18,14 @@ export default class ResponseBuilder {
|
||||
return this
|
||||
}
|
||||
|
||||
private _headers: Headers = new Headers()
|
||||
public headers(headers: HeadersInit ) {
|
||||
if (headers instanceof Headers) {
|
||||
private _headers: Record<string, string> = {}
|
||||
public headers(headers: Record<string, string>) {
|
||||
this._headers = headers
|
||||
} else {
|
||||
this._headers = new Headers(headers)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
public addHeader(key: string, value: string) {
|
||||
this._headers.append(key, value)
|
||||
this._headers[key] = value
|
||||
return this
|
||||
}
|
||||
|
||||
@ -41,7 +37,7 @@ export default class ResponseBuilder {
|
||||
}
|
||||
|
||||
public removeHeader(key: string) {
|
||||
this._headers.delete(key)
|
||||
delete this._headers[key]
|
||||
return this
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
* @returns a number if parsing happened correctly or undefined
|
||||
*/
|
||||
function parseNumber(str: string): number | undefined {
|
||||
if (!/^(\d|\.)+$/g.test(str)) {
|
||||
if (!/^-?(\d|\.)+$/g.test(str)) {
|
||||
return undefined
|
||||
}
|
||||
const float = parseFloat(str)
|
||||
@ -53,10 +53,10 @@ export function getParams(data: string) {
|
||||
const lines = data.split('\n').filter((it) => it.startsWith(';') && it.includes('='))
|
||||
// create the config object
|
||||
const obj: Record<string, string | number> = {}
|
||||
// loop through eacj config
|
||||
// loop through each config
|
||||
for (const line of lines) {
|
||||
// get its key and value
|
||||
const [key, value] = line.split('=', 2).map((it) => it.slice(1).trim())
|
||||
const [key, value] = line.slice(1).split(/ *= */, 2).map((it) => it.trim())
|
||||
// sip if it has no key or value
|
||||
if (!key || !value) {
|
||||
continue
|
||||
@ -78,10 +78,10 @@ export function getParams(data: string) {
|
||||
if (offset > 0) {
|
||||
realKey = `${realKey}_${offset}`
|
||||
}
|
||||
// detect key collisions
|
||||
if (obj[realKey] && obj[realKey] !== realValue) {
|
||||
throw new Error(`Key collision ${key}=${realValue} ${realKey}=${obj[realKey]}`)
|
||||
}
|
||||
// detect key collisions (it will never happens with the while above)
|
||||
// if (obj[realKey] && obj[realKey] !== realValue) {
|
||||
// throw new Error(`Key collision ${key}=${realValue} ${realKey}=${obj[realKey]}`)
|
||||
// }
|
||||
// set the value to the key
|
||||
obj[realKey] = realValue
|
||||
// transform the time to a number of seconds
|
||||
|
@ -1,5 +1,3 @@
|
||||
import Client from './Client'
|
||||
|
||||
/**
|
||||
* the Dao is the object that connect the Database or source to the application layer
|
||||
*
|
||||
|
@ -3,7 +3,13 @@ import Passthrough from '../components/Passthrough.astro'
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import DaoFactory from '../models/DaoFactory'
|
||||
|
||||
const user = await DaoFactory.get('user').get('648f81f857503c7d29465318')
|
||||
const session = DaoFactory.get('session').getSession(Astro.request)
|
||||
|
||||
if (!session) {
|
||||
return Astro.redirect('/')
|
||||
}
|
||||
|
||||
const user = await DaoFactory.get('user').get(session.userId)
|
||||
const list = await DaoFactory.get('apiKey').findAll({
|
||||
user: user!.id
|
||||
})
|
||||
|
@ -7,12 +7,9 @@ import { spawn } from 'node:child_process'
|
||||
import fs from 'node:fs/promises'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import StatusCode from '../../../../libs/HTTP/StatusCode'
|
||||
import { buildRFC7807 } from '../../../../libs/RFCs/RFC7807'
|
||||
import RateLimiter from '../../../../libs/RateLimiter'
|
||||
import ResponseBuilder from '../../../../libs/ResponseBuilder'
|
||||
import StatusCode from '../../../../libs/StatusCode'
|
||||
import { getParams } from '../../../../libs/gcodeUtils'
|
||||
import { validateAuth } from '../../../../libs/validateAuth'
|
||||
import DaoFactory from '../../../../models/DaoFactory'
|
||||
|
||||
interface SliceError {
|
||||
@ -20,7 +17,6 @@ interface SliceError {
|
||||
output: Array<string>
|
||||
}
|
||||
|
||||
|
||||
let tmpDir: string
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { objectOmit } from '@dzeio/object-util'
|
||||
import type { APIRoute } from 'astro'
|
||||
import StatusCode from '../../../../../../libs/HTTP/StatusCode'
|
||||
import { buildRFC7807 } from '../../../../../../libs/RFCs/RFC7807'
|
||||
import StatusCode from '../../../../../../libs/StatusCode'
|
||||
import DaoFactory from '../../../../../../models/DaoFactory'
|
||||
|
||||
export const post: APIRoute = async ({ params, request, locals }) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { APIRoute } from 'astro'
|
||||
import crypto from 'node:crypto'
|
||||
import StatusCode from '../../../../../../libs/StatusCode'
|
||||
import StatusCode from '../../../../../../libs/HTTP/StatusCode'
|
||||
import DaoFactory from '../../../../../../models/DaoFactory'
|
||||
|
||||
export const post: APIRoute = async ({ params, locals }) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { APIRoute } from 'astro'
|
||||
import StatusCode from '../../../../libs/HTTP/StatusCode'
|
||||
import { buildRFC7807 } from '../../../../libs/RFCs/RFC7807'
|
||||
import StatusCode from '../../../../libs/StatusCode'
|
||||
|
||||
export const get: APIRoute = async ({ locals }) => {
|
||||
return locals.responseBuilder
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
import Layout from '../../layouts/Layout.astro'
|
||||
import { getEntry } from 'astro:content'
|
||||
import StatusCode from '../../libs/StatusCode'
|
||||
import StatusCode from '../../libs/HTTP/StatusCode'
|
||||
|
||||
const page = Astro.params.page
|
||||
|
||||
|
41
test.ts
41
test.ts
@ -1,41 +0,0 @@
|
||||
import DaoFactory from './src/models/DaoFactory'
|
||||
|
||||
(async () => {
|
||||
// await DaoFactory.get('config').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('user').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('config').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('user').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('config').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('config').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('config').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('user').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('config').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('config').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('user').create({user: {id: 'pouet'}})
|
||||
// await DaoFactory.get('user').create({user: {id: 'pouet'}})
|
||||
|
||||
const dao = DaoFactory.get('user')
|
||||
console.log(await dao.create({email: 'pokemon@go.com'}))
|
||||
const obj = await dao.get('648f82be60a03b7398d36925')
|
||||
console.log(obj)
|
||||
if (!obj) {
|
||||
console.log('no obj :(')
|
||||
} else {
|
||||
console.log('object :)', obj)
|
||||
obj.email += 'jesuisundieu@pokemon.com'
|
||||
console.log(await dao.update(obj))
|
||||
}
|
||||
|
||||
const toDelete = await dao.findOne({email: 'pokemon@go.com'})
|
||||
if (toDelete) {
|
||||
console.log('todelete :)', toDelete)
|
||||
await dao.delete(toDelete)
|
||||
}
|
||||
|
||||
console.log()
|
||||
console.log('done')
|
||||
process.exit(0)
|
||||
})()
|
||||
|
||||
// await mongoose.get('id')
|
||||
// await fetch(`/api/users/${'id'}`).then((it) => it.json())
|
22
tests/libs/authUtils.test.ts
Normal file
22
tests/libs/authUtils.test.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { comparePassword, hashPassword } from '../../src/libs/AuthUtils'
|
||||
|
||||
|
||||
describe('AuthUtils', () => {
|
||||
it('should hash the password', async () => {
|
||||
expect(await hashPassword('test')).toBeDefined()
|
||||
})
|
||||
|
||||
it('should compared succeffully password', async () => {
|
||||
const pass = 'test'
|
||||
const hash = await hashPassword(pass)
|
||||
expect(await comparePassword(pass, hash)).toBe(true)
|
||||
})
|
||||
|
||||
it('should not generate twice the same hash', async () => {
|
||||
const pass = 'test'
|
||||
const hash1 = await hashPassword(pass)
|
||||
const hash2 = await hashPassword(pass)
|
||||
expect(hash1).not.toBe(hash2)
|
||||
})
|
||||
})
|
50
tests/libs/gcodeUtilts.test.ts
Normal file
50
tests/libs/gcodeUtilts.test.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { afterEach, assert, beforeEach, describe, expect, it, test, vi } from 'vitest'
|
||||
import { getParams } from '../../src/libs/gcodeUtils'
|
||||
|
||||
|
||||
describe('gcodeUtils', () => {
|
||||
it('should get parameters', () => {
|
||||
const gcode = `
|
||||
balfs
|
||||
dgfdf
|
||||
sd
|
||||
httphq
|
||||
estimated_printing_time_normal_modewef
|
||||
|
||||
; test=a
|
||||
fgd
|
||||
;test =b
|
||||
;test= c
|
||||
;test = d
|
||||
;number=1.12
|
||||
;number2=-1
|
||||
;invalid=
|
||||
;invalid
|
||||
;estimated_printing_time_normal_mode=1d 1h 1m 1s
|
||||
|
||||
sdffgaf
|
||||
fgsdf
|
||||
g
|
||||
sfd
|
||||
hh
|
||||
ehf
|
||||
`
|
||||
expect(getParams(gcode)).toEqual({
|
||||
test: 'a',
|
||||
test_1: 'b',
|
||||
test_2: 'c',
|
||||
test_3: 'd',
|
||||
number: 1.12,
|
||||
number2: -1,
|
||||
estimated_printing_time_normal_mode: '1d 1h 1m 1s',
|
||||
estimated_printing_time_seconds: 90061
|
||||
})
|
||||
})
|
||||
|
||||
it('should get parameters', () => {
|
||||
const gcode = `
|
||||
;estimated_printing_time_normal_mode=1w 1d 1h 1m 1s
|
||||
`
|
||||
expect(() => getParams(gcode)).toThrow(/1w/g)
|
||||
})
|
||||
})
|
122
tests/libs/rateLimit.test.ts
Normal file
122
tests/libs/rateLimit.test.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import { afterEach, assert, beforeEach, describe, expect, it, test, vi } from 'vitest'
|
||||
import RateLimiter from '../../src/libs/RateLimiter'
|
||||
|
||||
|
||||
// Mock Response
|
||||
vi.stubGlobal('Response', class {
|
||||
public constructor(
|
||||
public body?: any,
|
||||
public init?: any
|
||||
) {}
|
||||
})
|
||||
|
||||
describe('Rate Limit', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
})
|
||||
|
||||
|
||||
it('should work with global variant', () => {
|
||||
const now1 = new Date(1000)
|
||||
vi.setSystemTime(now1)
|
||||
const limit = RateLimiter.getInstance()
|
||||
|
||||
const result1 = limit.consume('key')
|
||||
expect(result1).toEqual({
|
||||
"X-RateLimit-Limit": '10',
|
||||
"X-RateLimit-Remaining": '9',
|
||||
"X-RateLimit-Reset": '61'
|
||||
})
|
||||
const limit2 = RateLimiter.getInstance()
|
||||
const result2 = limit2.consume('key')
|
||||
expect(result2).toEqual({
|
||||
"X-RateLimit-Limit": '10',
|
||||
"X-RateLimit-Remaining": '8',
|
||||
"X-RateLimit-Reset": '61'
|
||||
})
|
||||
})
|
||||
|
||||
it('should consume points', () => {
|
||||
const now1 = new Date(1000)
|
||||
vi.setSystemTime(now1)
|
||||
const limit = new RateLimiter(2, 10)
|
||||
|
||||
const result1 = limit.consume('key')
|
||||
expect(result1).toEqual({
|
||||
"X-RateLimit-Limit": '2',
|
||||
"X-RateLimit-Remaining": '1',
|
||||
"X-RateLimit-Reset": '11'
|
||||
})
|
||||
const result2 = limit.consume('key')
|
||||
expect(result2).toEqual({
|
||||
"X-RateLimit-Limit": '2',
|
||||
"X-RateLimit-Remaining": '0',
|
||||
"X-RateLimit-Reset": '11'
|
||||
})
|
||||
})
|
||||
|
||||
it('should rate limit', () => {
|
||||
const now1 = new Date(1000)
|
||||
vi.setSystemTime(now1)
|
||||
const limit = new RateLimiter(1, 10)
|
||||
|
||||
const result1 = limit.consume('key')
|
||||
expect(result1).toEqual({
|
||||
"X-RateLimit-Limit": '1',
|
||||
"X-RateLimit-Remaining": '0',
|
||||
"X-RateLimit-Reset": '11'
|
||||
})
|
||||
const result2 = limit.consume('key')
|
||||
expect(result2).instanceOf(Response)
|
||||
const result3 = limit.consume('key')
|
||||
expect(result3).instanceOf(Response)
|
||||
})
|
||||
|
||||
it('should reset after some time', () => {
|
||||
const now1 = new Date(1000)
|
||||
vi.setSystemTime(now1)
|
||||
const limit = new RateLimiter(2, 10)
|
||||
|
||||
const result1 = limit.consume('key')
|
||||
expect(result1).toEqual({
|
||||
"X-RateLimit-Limit": '2',
|
||||
"X-RateLimit-Remaining": '1',
|
||||
"X-RateLimit-Reset": '11'
|
||||
})
|
||||
const result2 = limit.consume('key')
|
||||
expect(result2).toEqual({
|
||||
"X-RateLimit-Limit": '2',
|
||||
"X-RateLimit-Remaining": '0',
|
||||
"X-RateLimit-Reset": '11'
|
||||
})
|
||||
|
||||
|
||||
const result3 = limit.consume('key')
|
||||
expect(result3).instanceOf(Response)
|
||||
|
||||
const now2 = new Date(11000)
|
||||
vi.setSystemTime(now2)
|
||||
const result4 = limit.consume('key')
|
||||
expect(result4).toEqual({
|
||||
"X-RateLimit-Limit": '2',
|
||||
"X-RateLimit-Remaining": '1',
|
||||
"X-RateLimit-Reset": '21'
|
||||
})
|
||||
|
||||
|
||||
const result5 = limit.consume('key')
|
||||
expect(result5).toEqual({
|
||||
"X-RateLimit-Limit": '2',
|
||||
"X-RateLimit-Remaining": '0',
|
||||
"X-RateLimit-Reset": '21'
|
||||
})
|
||||
|
||||
|
||||
const result6 = limit.consume('key')
|
||||
expect(result6).instanceOf(Response)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user