Merge branch 'dev' into feat/232-assignments-pagina-ui-ux
Merge dev into feat/232-assignments-pagina-ui-ux
This commit is contained in:
commit
96076844a5
56 changed files with 1265 additions and 867 deletions
|
@ -13,7 +13,7 @@
|
||||||
#DWENGO_LEARNING_CONTENT_REPO_API_BASE_URL=https://dwengo.org/backend/api
|
#DWENGO_LEARNING_CONTENT_REPO_API_BASE_URL=https://dwengo.org/backend/api
|
||||||
# The default fallback language.
|
# The default fallback language.
|
||||||
#DWENGO_FALLBACK_LANGUAGE=nl
|
#DWENGO_FALLBACK_LANGUAGE=nl
|
||||||
# Whether running in production mode or not. Possible values are "prod" or "dev".
|
# Whether running in production mode or not. Possible values are "prod", "staging", "test" or "dev".
|
||||||
#DWENGO_RUN_MODE=dev
|
#DWENGO_RUN_MODE=dev
|
||||||
|
|
||||||
# ! Change this! The hostname or IP address of the database
|
# ! Change this! The hostname or IP address of the database
|
||||||
|
@ -66,3 +66,12 @@ DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://hostname/realms/teacher/protocol/openid
|
||||||
# The address of the Loki instance, a log aggregation system.
|
# The address of the Loki instance, a log aggregation system.
|
||||||
# If running your stack in docker, this should use the docker service name.
|
# If running your stack in docker, this should use the docker service name.
|
||||||
#DWENGO_LOGGING_LOKI_HOST=http://localhost:3102
|
#DWENGO_LOGGING_LOKI_HOST=http://localhost:3102
|
||||||
|
|
||||||
|
# The hostname or IP address of the caching server, e.g. Memcached.
|
||||||
|
# If running your stack in docker, this should use the docker service name.
|
||||||
|
#DWENGO_CACHE_HOST=localhost
|
||||||
|
#DWENGO_CACHE_PORT=11211
|
||||||
|
# The time-to-live (TTL) for cached items in seconds.
|
||||||
|
#DWENGO_CACHE_TTL=3600
|
||||||
|
# If your cache server benefits from a prefix, you can set it here.
|
||||||
|
#DWENGO_CACHE_KEY_PREFIX=dwengo
|
||||||
|
|
|
@ -35,3 +35,7 @@ DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://idp:7080/idp/realms/teacher/protocol/op
|
||||||
|
|
||||||
DWENGO_LOGGING_LEVEL=info
|
DWENGO_LOGGING_LEVEL=info
|
||||||
DWENGO_LOGGING_LOKI_HOST=http://logging:3102
|
DWENGO_LOGGING_LOKI_HOST=http://logging:3102
|
||||||
|
|
||||||
|
DWENGO_CACHE_HOST=caching
|
||||||
|
#DWENGO_CACHE_PORT=11211
|
||||||
|
DWENGO_CACHE_TTL=604800
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
DWENGO_RUN_MODE=staging
|
||||||
|
|
||||||
DWENGO_DB_HOST=db
|
DWENGO_DB_HOST=db
|
||||||
DWENGO_DB_PORT=5432
|
DWENGO_DB_PORT=5432
|
||||||
DWENGO_DB_USERNAME=postgres
|
DWENGO_DB_USERNAME=postgres
|
||||||
|
@ -18,4 +20,8 @@ DWENGO_CORS_ALLOWED_ORIGINS=http://localhost/*,http://idp:7080,https://idp:7080
|
||||||
|
|
||||||
# Logging and monitoring
|
# Logging and monitoring
|
||||||
|
|
||||||
LOKI_HOST=http://logging:3102
|
DWENGO_LOGGING_LEVEL=debug
|
||||||
|
DWENGO_LOGGING_LOKI_HOST=http://logging:3102
|
||||||
|
|
||||||
|
DWENGO_CACHE_HOST=caching
|
||||||
|
DWENGO_CACHE_TTL=86400
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
# Should not need to be modified.
|
# Should not need to be modified.
|
||||||
# See .env.example for more information.
|
# See .env.example for more information.
|
||||||
#
|
#
|
||||||
|
### Advanced configuration ###
|
||||||
|
|
||||||
|
|
||||||
### Dwengo ###
|
### Dwengo ###
|
||||||
|
|
||||||
|
@ -21,3 +23,7 @@ DWENGO_AUTH_TEACHER_CLIENT_ID=dwengo
|
||||||
DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://localhost:7080/realms/teacher/protocol/openid-connect/certs
|
DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://localhost:7080/realms/teacher/protocol/openid-connect/certs
|
||||||
|
|
||||||
DWENGO_CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000,http://localhost:9876,*
|
DWENGO_CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000,http://localhost:9876,*
|
||||||
|
|
||||||
|
### Advanced configuration ###
|
||||||
|
|
||||||
|
DWENGO_LOGGING_LEVEL=debug
|
||||||
|
|
|
@ -27,6 +27,13 @@ FROM node:22 AS production-stage
|
||||||
|
|
||||||
WORKDIR /app/dwengo
|
WORKDIR /app/dwengo
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
COPY backend/package.json ./backend/
|
||||||
|
# Backend depends on common
|
||||||
|
COPY common/package.json ./common/
|
||||||
|
|
||||||
|
RUN npm install --silent --only=production
|
||||||
|
|
||||||
# Copy static files
|
# Copy static files
|
||||||
|
|
||||||
COPY ./backend/i18n ./i18n
|
COPY ./backend/i18n ./i18n
|
||||||
|
@ -37,15 +44,6 @@ COPY --from=build-stage /app/dwengo/common/dist ./common/dist
|
||||||
COPY --from=build-stage /app/dwengo/backend/dist ./backend/dist
|
COPY --from=build-stage /app/dwengo/backend/dist ./backend/dist
|
||||||
COPY --from=build-stage /app/dwengo/docs/api/swagger.json ./docs/api/swagger.json
|
COPY --from=build-stage /app/dwengo/docs/api/swagger.json ./docs/api/swagger.json
|
||||||
|
|
||||||
COPY package*.json ./
|
|
||||||
COPY backend/package.json ./backend/
|
|
||||||
# Backend depends on common
|
|
||||||
COPY common/package.json ./common/
|
|
||||||
|
|
||||||
RUN npm install --silent --only=production
|
|
||||||
|
|
||||||
COPY ./backend/i18n ./backend/i18n
|
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
CMD ["node", "--env-file=/app/dwengo/backend/.env", "/app/dwengo/backend/dist/app.js"]
|
CMD ["node", "--env-file=/app/dwengo/backend/.env", "/app/dwengo/backend/dist/app.js"]
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"jwks-rsa": "^3.1.0",
|
"jwks-rsa": "^3.1.0",
|
||||||
"loki-logger-ts": "^1.0.2",
|
"loki-logger-ts": "^1.0.2",
|
||||||
"marked": "^15.0.7",
|
"marked": "^15.0.7",
|
||||||
|
"memjs": "^1.3.2",
|
||||||
"mime-types": "^3.0.1",
|
"mime-types": "^3.0.1",
|
||||||
"nanoid": "^5.1.5",
|
"nanoid": "^5.1.5",
|
||||||
"response-time": "^2.3.3",
|
"response-time": "^2.3.3",
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/express-fileupload": "^1.5.1",
|
"@types/express-fileupload": "^1.5.1",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
"@types/memjs": "^1.3.3",
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.13.4",
|
||||||
"@types/response-time": "^2.3.8",
|
"@types/response-time": "^2.3.8",
|
||||||
|
|
44
backend/src/caching.ts
Normal file
44
backend/src/caching.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { getLogger } from './logging/initalize.js';
|
||||||
|
import { envVars, getEnvVar } from './util/envVars.js';
|
||||||
|
import { Client } from 'memjs';
|
||||||
|
|
||||||
|
export type CacheClient = Client;
|
||||||
|
|
||||||
|
let cacheClient: CacheClient;
|
||||||
|
|
||||||
|
async function initializeClient(): Promise<CacheClient> {
|
||||||
|
if (cacheClient !== undefined) {
|
||||||
|
return cacheClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachingHost = getEnvVar(envVars.CacheHost);
|
||||||
|
const cachingPort = getEnvVar(envVars.CachePort);
|
||||||
|
const cachingUrl = `${cachingHost}:${cachingPort}`;
|
||||||
|
|
||||||
|
if (cachingHost === '') {
|
||||||
|
return cacheClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheClient = Client.create(cachingUrl);
|
||||||
|
|
||||||
|
getLogger().info(`Memcached client initialized at ${cachingUrl}`);
|
||||||
|
|
||||||
|
return cacheClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCacheClient(): Promise<CacheClient> {
|
||||||
|
cacheClient ||= await initializeClient();
|
||||||
|
return cacheClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkCachingHealth(): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const client = await getCacheClient();
|
||||||
|
await client.set('health', Buffer.from('ok'), { expires: 60 });
|
||||||
|
const reply = await cacheClient.get('health');
|
||||||
|
return reply?.value?.toString() === 'ok';
|
||||||
|
} catch (error) {
|
||||||
|
getLogger().error('Caching Health Check Failed:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,8 @@ function initializeLogger(): Logger {
|
||||||
format: format.combine(format.cli(), format.simple()),
|
format: format.combine(format.cli(), format.simple()),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (getEnvVar(envVars.RunMode) === 'dev') {
|
const runMode = getEnvVar(envVars.RunMode);
|
||||||
|
if (runMode === 'dev' || runMode.includes('test')) {
|
||||||
logger = createLogger({
|
logger = createLogger({
|
||||||
transports: [consoleTransport],
|
transports: [consoleTransport],
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,12 +7,17 @@ let orm: MikroORM | undefined;
|
||||||
export async function initORM(testingMode = false): Promise<MikroORM<IDatabaseDriver, EntityManager>> {
|
export async function initORM(testingMode = false): Promise<MikroORM<IDatabaseDriver, EntityManager>> {
|
||||||
const logger: Logger = getLogger();
|
const logger: Logger = getLogger();
|
||||||
|
|
||||||
logger.info('Initializing ORM');
|
const options = config(testingMode);
|
||||||
logger.debug('MikroORM config is', config);
|
|
||||||
|
logger.info('MikroORM config is', options);
|
||||||
|
|
||||||
|
logger.info('Initializing ORM');
|
||||||
|
orm = await MikroORM.init(options);
|
||||||
|
logger.info('MikroORM initialized');
|
||||||
|
|
||||||
orm = await MikroORM.init(config(testingMode));
|
|
||||||
// Update the database scheme if necessary and enabled.
|
// Update the database scheme if necessary and enabled.
|
||||||
if (getEnvVar(envVars.DbUpdate)) {
|
if (getEnvVar(envVars.DbUpdate)) {
|
||||||
|
logger.info('MikroORM: Updating database schema');
|
||||||
await orm.schema.updateSchema();
|
await orm.schema.updateSchema();
|
||||||
} else {
|
} else {
|
||||||
const diff = await orm.schema.getUpdateSchemaSQL();
|
const diff = await orm.schema.getUpdateSchemaSQL();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { DWENGO_API_BASE } from '../config.js';
|
import { DWENGO_API_BASE } from '../config.js';
|
||||||
import { fetchWithLogging } from '../util/api-helper.js';
|
import { fetchRemote } from '../util/api-helper.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FilteredLearningObject,
|
FilteredLearningObject,
|
||||||
|
@ -39,10 +39,7 @@ function filterData(data: LearningObjectMetadata, htmlUrl: string): FilteredLear
|
||||||
*/
|
*/
|
||||||
export async function getLearningObjectById(hruid: string, language: string): Promise<FilteredLearningObject | null> {
|
export async function getLearningObjectById(hruid: string, language: string): Promise<FilteredLearningObject | null> {
|
||||||
const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata?hruid=${hruid}&language=${language}`;
|
const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata?hruid=${hruid}&language=${language}`;
|
||||||
const metadata = await fetchWithLogging<LearningObjectMetadata>(
|
const metadata = await fetchRemote<LearningObjectMetadata>(metadataUrl, `Metadata for Learning Object HRUID "${hruid}" (language ${language})`);
|
||||||
metadataUrl,
|
|
||||||
`Metadata for Learning Object HRUID "${hruid}" (language ${language})`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!metadata) {
|
if (!metadata) {
|
||||||
getLogger().error(`⚠️ WARNING: Learning object "${hruid}" not found.`);
|
getLogger().error(`⚠️ WARNING: Learning object "${hruid}" not found.`);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { DWENGO_API_BASE } from '../../config.js';
|
import { DWENGO_API_BASE } from '../../config.js';
|
||||||
import { fetchWithLogging } from '../../util/api-helper.js';
|
import { fetchRemote } from '../../util/api-helper.js';
|
||||||
import dwengoApiLearningPathProvider from '../learning-paths/dwengo-api-learning-path-provider.js';
|
import dwengoApiLearningPathProvider from '../learning-paths/dwengo-api-learning-path-provider.js';
|
||||||
import { LearningObjectProvider } from './learning-object-provider.js';
|
import { LearningObjectProvider } from './learning-object-provider.js';
|
||||||
import { getLogger, Logger } from '../../logging/initalize.js';
|
import { getLogger, Logger } from '../../logging/initalize.js';
|
||||||
|
@ -88,7 +88,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = {
|
||||||
*/
|
*/
|
||||||
async getLearningObjectById(id: LearningObjectIdentifierDTO): Promise<FilteredLearningObject | null> {
|
async getLearningObjectById(id: LearningObjectIdentifierDTO): Promise<FilteredLearningObject | null> {
|
||||||
const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata`;
|
const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata`;
|
||||||
const metadata = await fetchWithLogging<LearningObjectMetadata>(
|
const metadata = await fetchRemote<LearningObjectMetadata>(
|
||||||
metadataUrl,
|
metadataUrl,
|
||||||
`Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`,
|
`Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`,
|
||||||
{
|
{
|
||||||
|
@ -124,7 +124,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = {
|
||||||
*/
|
*/
|
||||||
async getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null> {
|
async getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null> {
|
||||||
const htmlUrl = `${DWENGO_API_BASE}/learningObject/getRaw`;
|
const htmlUrl = `${DWENGO_API_BASE}/learningObject/getRaw`;
|
||||||
const html = await fetchWithLogging<string>(htmlUrl, `Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`, {
|
const html = await fetchRemote<string>(htmlUrl, `Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`, {
|
||||||
params: { ...id },
|
params: { ...id },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { fetchWithLogging } from '../../util/api-helper.js';
|
import { fetchRemote } from '../../util/api-helper.js';
|
||||||
import { DWENGO_API_BASE } from '../../config.js';
|
import { DWENGO_API_BASE } from '../../config.js';
|
||||||
import { LearningPathProvider } from './learning-path-provider.js';
|
import { LearningPathProvider } from './learning-path-provider.js';
|
||||||
import { getLogger, Logger } from '../../logging/initalize.js';
|
import { getLogger, Logger } from '../../logging/initalize.js';
|
||||||
|
@ -42,7 +42,7 @@ const dwengoApiLearningPathProvider: LearningPathProvider = {
|
||||||
const apiUrl = `${DWENGO_API_BASE}/learningPath/getPathsFromIdList`;
|
const apiUrl = `${DWENGO_API_BASE}/learningPath/getPathsFromIdList`;
|
||||||
const params = { pathIdList: JSON.stringify({ hruids }), language };
|
const params = { pathIdList: JSON.stringify({ hruids }), language };
|
||||||
|
|
||||||
const learningPaths = await fetchWithLogging<LearningPath[]>(apiUrl, `Learning paths for ${source}`, { params });
|
const learningPaths = await fetchRemote<LearningPath[]>(apiUrl, `Learning paths for ${source}`, { params });
|
||||||
|
|
||||||
if (!learningPaths || learningPaths.length === 0) {
|
if (!learningPaths || learningPaths.length === 0) {
|
||||||
logger.warn(`⚠️ WARNING: No learning paths found for ${source}.`);
|
logger.warn(`⚠️ WARNING: No learning paths found for ${source}.`);
|
||||||
|
@ -66,12 +66,11 @@ const dwengoApiLearningPathProvider: LearningPathProvider = {
|
||||||
const apiUrl = `${DWENGO_API_BASE}/learningPath/search`;
|
const apiUrl = `${DWENGO_API_BASE}/learningPath/search`;
|
||||||
const params = { all: query, language };
|
const params = { all: query, language };
|
||||||
|
|
||||||
const searchResults = await fetchWithLogging<LearningPath[]>(apiUrl, `Search learning paths with query "${query}"`, { params });
|
const searchResults = await fetchRemote<LearningPath[]>(apiUrl, `Search learning paths with query "${query}"`, { params });
|
||||||
|
|
||||||
if (searchResults) {
|
if (searchResults) {
|
||||||
await Promise.all(searchResults?.map(async (it) => addProgressToLearningPath(it, personalizedFor)));
|
await Promise.all(searchResults?.map(async (it) => addProgressToLearningPath(it, personalizedFor)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return searchResults ?? [];
|
return searchResults ?? [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,66 @@
|
||||||
import axios, { AxiosRequestConfig } from 'axios';
|
import axios, { AxiosRequestConfig } from 'axios';
|
||||||
import { getLogger, Logger } from '../logging/initalize.js';
|
import { getLogger, Logger } from '../logging/initalize.js';
|
||||||
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
|
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
|
||||||
|
import { getCacheClient } from '../caching.js';
|
||||||
|
import { envVars, getEnvVar, getNumericEnvVar } from './envVars.js';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
|
||||||
|
const cacheClient = await getCacheClient();
|
||||||
const logger: Logger = getLogger();
|
const logger: Logger = getLogger();
|
||||||
|
const runMode: string = getEnvVar(envVars.RunMode);
|
||||||
|
const prefix: string = getEnvVar(envVars.CacheKeyPrefix);
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
params?: Record<string, unknown> | LearningObjectIdentifier;
|
||||||
|
query?: Record<string, unknown>;
|
||||||
|
responseType?: 'json' | 'text';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility function to fetch data from an API endpoint with error handling.
|
* Utility function to fetch data from an API endpoint with error handling and caching.
|
||||||
* Logs errors but does NOT throw exceptions to keep the system running.
|
* Logs errors but does NOT throw exceptions to keep the system running.
|
||||||
*
|
*
|
||||||
* @param url The API endpoint to fetch from.
|
* @param url The API endpoint to fetch from.
|
||||||
* @param description A short description of what is being fetched (for logging).
|
* @param description A short description of what is being fetched (for logging).
|
||||||
* @param options Contains further options such as params (the query params) and responseType (whether the response
|
* @param options Contains further options such as params (the query params) and responseType (whether the response
|
||||||
* should be parsed as JSON ("json") or whether it should be returned as plain text ("text")
|
* should be parsed as JSON ("json") or whether it should be returned as plain text ("text")
|
||||||
|
* @param cacheTTL Time-to-live for the cache in seconds (default: 60 seconds).
|
||||||
* @returns The response data if successful, or null if an error occurs.
|
* @returns The response data if successful, or null if an error occurs.
|
||||||
*/
|
*/
|
||||||
export async function fetchWithLogging<T>(
|
export async function fetchRemote<T>(url: string, description: string, options?: Options, cacheTTL?: number): Promise<T | null> {
|
||||||
url: string,
|
if (runMode !== 'dev' && !runMode.includes('test') && cacheClient !== undefined) {
|
||||||
description: string,
|
return fetchWithCache<T>(url, description, options, cacheTTL);
|
||||||
options?: {
|
|
||||||
params?: Record<string, unknown> | LearningObjectIdentifier;
|
|
||||||
query?: Record<string, unknown>;
|
|
||||||
responseType?: 'json' | 'text';
|
|
||||||
}
|
}
|
||||||
): Promise<T | null> {
|
|
||||||
|
getLogger().debug(`🔄 INFO: Bypassing cache for ${description} at "${url}".`);
|
||||||
|
return fetchWithLogging(url, description, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchWithCache<T>(url: string, description: string, options?: Options, cacheTTL?: number): Promise<T | null> {
|
||||||
|
// Combine the URL and parameters to create a unique cache key.
|
||||||
|
// NOTE Using a hash function to keep the key short, since Memcached has a limit on key size
|
||||||
|
const urlWithParams = `${url}${options?.params ? JSON.stringify(options.params) : ''}`;
|
||||||
|
const hashedUrl = createHash('sha256').update(urlWithParams).digest('hex');
|
||||||
|
const key = `${prefix}:${hashedUrl}`;
|
||||||
|
|
||||||
|
const cachedData = await cacheClient.get(key);
|
||||||
|
|
||||||
|
if (cachedData?.value) {
|
||||||
|
logger.debug(`✅ INFO: Cache hit for ${description} at "${url}" (key: "${key}")`);
|
||||||
|
return JSON.parse(cachedData.value.toString()) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`🔄 INFO: Cache miss for ${description} at "${url}". Fetching data...`);
|
||||||
|
const response = await fetchWithLogging<T>(url, description, options);
|
||||||
|
|
||||||
|
const ttl = cacheTTL || getNumericEnvVar(envVars.CacheTTL);
|
||||||
|
await cacheClient.set(key, JSON.stringify(response), { expires: ttl });
|
||||||
|
logger.debug(`✅ INFO: Cached response for ${description} at "${url}" for ${ttl} seconds. (key: "${key}")`);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchWithLogging<T>(url: string, description: string, options?: Options): Promise<T | null> {
|
||||||
try {
|
try {
|
||||||
const config: AxiosRequestConfig = options || {};
|
const config: AxiosRequestConfig = options || {};
|
||||||
const response = await axios.get<T>(url, config);
|
const response = await axios.get<T>(url, config);
|
||||||
|
|
|
@ -5,6 +5,7 @@ const STUDENT_IDP_PREFIX = IDP_PREFIX + 'STUDENT_';
|
||||||
const TEACHER_IDP_PREFIX = IDP_PREFIX + 'TEACHER_';
|
const TEACHER_IDP_PREFIX = IDP_PREFIX + 'TEACHER_';
|
||||||
const CORS_PREFIX = PREFIX + 'CORS_';
|
const CORS_PREFIX = PREFIX + 'CORS_';
|
||||||
const LOGGING_PREFIX = PREFIX + 'LOGGING_';
|
const LOGGING_PREFIX = PREFIX + 'LOGGING_';
|
||||||
|
const CACHE_PREFIX = PREFIX + 'CACHE_';
|
||||||
|
|
||||||
interface EnvVar {
|
interface EnvVar {
|
||||||
key: string;
|
key: string;
|
||||||
|
@ -39,6 +40,11 @@ export const envVars: Record<string, EnvVar> = {
|
||||||
|
|
||||||
LogLevel: { key: LOGGING_PREFIX + 'LEVEL', defaultValue: 'info' },
|
LogLevel: { key: LOGGING_PREFIX + 'LEVEL', defaultValue: 'info' },
|
||||||
LokiHost: { key: LOGGING_PREFIX + 'LOKI_HOST', defaultValue: 'http://localhost:3102' },
|
LokiHost: { key: LOGGING_PREFIX + 'LOKI_HOST', defaultValue: 'http://localhost:3102' },
|
||||||
|
|
||||||
|
CacheHost: { key: CACHE_PREFIX + 'HOST' },
|
||||||
|
CachePort: { key: CACHE_PREFIX + 'PORT', defaultValue: 11211 },
|
||||||
|
CacheTTL: { key: CACHE_PREFIX + 'TTL', defaultValue: 60 * 60 * 24 }, // 24 hours
|
||||||
|
CacheKeyPrefix: { key: CACHE_PREFIX + 'KEY_PREFIX', defaultValue: 'dwengo' },
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,7 +62,7 @@ export function getEnvVar(envVar: EnvVar): string {
|
||||||
} else if (envVar.required) {
|
} else if (envVar.required) {
|
||||||
throw new Error(`Missing environment variable: ${envVar.key}`);
|
throw new Error(`Missing environment variable: ${envVar.key}`);
|
||||||
} else {
|
} else {
|
||||||
return String(envVar.defaultValue) || '';
|
return envVar.defaultValue !== undefined ? String(envVar.defaultValue) || '' : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Language } from '@dwengo-1/common/util/language';
|
||||||
import { getAllAnswersHandler, getAnswerHandler, updateAnswerHandler } from '../../src/controllers/answers';
|
import { getAllAnswersHandler, getAnswerHandler, updateAnswerHandler } from '../../src/controllers/answers';
|
||||||
import { BadRequestException } from '../../src/exceptions/bad-request-exception';
|
import { BadRequestException } from '../../src/exceptions/bad-request-exception';
|
||||||
import { NotFoundException } from '../../src/exceptions/not-found-exception';
|
import { NotFoundException } from '../../src/exceptions/not-found-exception';
|
||||||
|
import { getAnswer02 } from '../test_assets/questions/answers.testdata';
|
||||||
|
|
||||||
describe('Questions controllers', () => {
|
describe('Questions controllers', () => {
|
||||||
let req: Partial<Request>;
|
let req: Partial<Request>;
|
||||||
|
@ -24,9 +25,14 @@ describe('Questions controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get answers list', async () => {
|
it('Get answers list', async () => {
|
||||||
|
const a = getAnswer02();
|
||||||
req = {
|
req = {
|
||||||
params: { hruid: 'id05', version: '1', seq: '2' },
|
params: {
|
||||||
query: { lang: Language.English, full: 'true' },
|
hruid: a.toQuestion.learningObjectHruid,
|
||||||
|
version: a.toQuestion.learningObjectVersion.toString(),
|
||||||
|
seq: a.toQuestion.sequenceNumber!.toString(),
|
||||||
|
},
|
||||||
|
query: { lang: a.toQuestion.learningObjectLanguage, full: 'true' },
|
||||||
};
|
};
|
||||||
|
|
||||||
await getAllAnswersHandler(req as Request, res as Response);
|
await getAllAnswersHandler(req as Request, res as Response);
|
||||||
|
@ -38,9 +44,15 @@ describe('Questions controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get answer', async () => {
|
it('Get answer', async () => {
|
||||||
|
const a = getAnswer02();
|
||||||
req = {
|
req = {
|
||||||
params: { hruid: 'id05', version: '1', seq: '2', seqAnswer: '2' },
|
params: {
|
||||||
query: { lang: Language.English, full: 'true' },
|
hruid: a.toQuestion.learningObjectHruid,
|
||||||
|
version: a.toQuestion.learningObjectVersion.toString(),
|
||||||
|
seq: a.toQuestion.sequenceNumber!.toString(),
|
||||||
|
seqAnswer: a.sequenceNumber!.toString(),
|
||||||
|
},
|
||||||
|
query: { lang: a.toQuestion.learningObjectLanguage, full: 'true' },
|
||||||
};
|
};
|
||||||
|
|
||||||
await getAnswerHandler(req as Request, res as Response);
|
await getAnswerHandler(req as Request, res as Response);
|
||||||
|
@ -68,11 +80,19 @@ describe('Questions controllers', () => {
|
||||||
await expect(async () => getAnswerHandler(req as Request, res as Response)).rejects.toThrow(BadRequestException);
|
await expect(async () => getAnswerHandler(req as Request, res as Response)).rejects.toThrow(BadRequestException);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Update question', async () => {
|
it('Update answer', async () => {
|
||||||
const newContent = 'updated question';
|
const a = getAnswer02();
|
||||||
|
const q = a.toQuestion;
|
||||||
|
|
||||||
|
const newContent = 'updated answer';
|
||||||
req = {
|
req = {
|
||||||
params: { hruid: 'id05', version: '1', seq: '2', seqAnswer: '2' },
|
params: {
|
||||||
query: { lang: Language.English },
|
hruid: q.learningObjectHruid,
|
||||||
|
version: q.learningObjectVersion.toString(),
|
||||||
|
seq: q.sequenceNumber!.toString(),
|
||||||
|
seqAnswer: a.sequenceNumber!.toString(),
|
||||||
|
},
|
||||||
|
query: { lang: q.learningObjectLanguage },
|
||||||
body: { content: newContent },
|
body: { content: newContent },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { getAllQuestionsHandler, getQuestionHandler, updateQuestionHandler } fro
|
||||||
import { Language } from '@dwengo-1/common/util/language';
|
import { Language } from '@dwengo-1/common/util/language';
|
||||||
import { NotFoundException } from '../../src/exceptions/not-found-exception';
|
import { NotFoundException } from '../../src/exceptions/not-found-exception';
|
||||||
import { BadRequestException } from '../../src/exceptions/bad-request-exception';
|
import { BadRequestException } from '../../src/exceptions/bad-request-exception';
|
||||||
|
import { getQuestion01 } from '../test_assets/questions/questions.testdata';
|
||||||
|
|
||||||
describe('Questions controllers', () => {
|
describe('Questions controllers', () => {
|
||||||
let req: Partial<Request>;
|
let req: Partial<Request>;
|
||||||
|
@ -24,9 +25,10 @@ describe('Questions controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get question list', async () => {
|
it('Get question list', async () => {
|
||||||
|
const q = getQuestion01();
|
||||||
req = {
|
req = {
|
||||||
params: { hruid: 'id05', version: '1' },
|
params: { hruid: q.learningObjectHruid, version: q.learningObjectVersion.toString() },
|
||||||
query: { lang: Language.English, full: 'true' },
|
query: { lang: q.learningObjectLanguage, full: 'true' },
|
||||||
};
|
};
|
||||||
|
|
||||||
await getAllQuestionsHandler(req as Request, res as Response);
|
await getAllQuestionsHandler(req as Request, res as Response);
|
||||||
|
@ -38,9 +40,10 @@ describe('Questions controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get question', async () => {
|
it('Get question', async () => {
|
||||||
|
const q = getQuestion01();
|
||||||
req = {
|
req = {
|
||||||
params: { hruid: 'id05', version: '1', seq: '1' },
|
params: { hruid: q.learningObjectHruid, version: q.learningObjectVersion.toString(), seq: q.sequenceNumber!.toString() },
|
||||||
query: { lang: Language.English, full: 'true' },
|
query: { lang: q.learningObjectLanguage, full: 'true' },
|
||||||
};
|
};
|
||||||
|
|
||||||
await getQuestionHandler(req as Request, res as Response);
|
await getQuestionHandler(req as Request, res as Response);
|
||||||
|
@ -51,8 +54,9 @@ describe('Questions controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get question with fallback sequence number and version', async () => {
|
it('Get question with fallback sequence number and version', async () => {
|
||||||
|
const q = getQuestion01();
|
||||||
req = {
|
req = {
|
||||||
params: { hruid: 'id05' },
|
params: { hruid: q.learningObjectHruid },
|
||||||
query: { lang: Language.English, full: 'true' },
|
query: { lang: Language.English, full: 'true' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,10 +103,11 @@ describe('Questions controllers', () => {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
it('Update question', async () => {
|
it('Update question', async () => {
|
||||||
|
const q = getQuestion01();
|
||||||
const newContent = 'updated question';
|
const newContent = 'updated question';
|
||||||
req = {
|
req = {
|
||||||
params: { hruid: 'id05', version: '1', seq: '1' },
|
params: { hruid: q.learningObjectHruid, version: q.learningObjectVersion.toString(), seq: q.sequenceNumber!.toString() },
|
||||||
query: { lang: Language.English },
|
query: { lang: q.learningObjectLanguage },
|
||||||
body: { content: newContent },
|
body: { content: newContent },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,17 @@ import {
|
||||||
deleteClassJoinRequestHandler,
|
deleteClassJoinRequestHandler,
|
||||||
getStudentRequestHandler,
|
getStudentRequestHandler,
|
||||||
} from '../../src/controllers/students.js';
|
} from '../../src/controllers/students.js';
|
||||||
import { TEST_STUDENTS } from '../test_assets/users/students.testdata.js';
|
import { getDireStraits, getNoordkaap, getTheDoors, TEST_STUDENTS } from '../test_assets/users/students.testdata.js';
|
||||||
import { NotFoundException } from '../../src/exceptions/not-found-exception.js';
|
import { NotFoundException } from '../../src/exceptions/not-found-exception.js';
|
||||||
import { BadRequestException } from '../../src/exceptions/bad-request-exception.js';
|
import { BadRequestException } from '../../src/exceptions/bad-request-exception.js';
|
||||||
import { ConflictException } from '../../src/exceptions/conflict-exception.js';
|
import { ConflictException } from '../../src/exceptions/conflict-exception.js';
|
||||||
import { EntityAlreadyExistsException } from '../../src/exceptions/entity-already-exists-exception.js';
|
import { EntityAlreadyExistsException } from '../../src/exceptions/entity-already-exists-exception.js';
|
||||||
import { StudentDTO } from '@dwengo-1/common/interfaces/student';
|
import { StudentDTO } from '@dwengo-1/common/interfaces/student';
|
||||||
import { getClass02 } from '../test_assets/classes/classes.testdata';
|
import { getClass02 } from '../test_assets/classes/classes.testdata.js';
|
||||||
|
import { getClassJoinRequest02 } from '../test_assets/classes/class-join-requests.testdata.js';
|
||||||
|
import { getTestGroup01 } from '../test_assets/assignments/groups.testdata.js';
|
||||||
|
import { getSubmission01 } from '../test_assets/assignments/submission.testdata.js';
|
||||||
|
import { getQuestion01 } from '../test_assets/questions/questions.testdata.js';
|
||||||
|
|
||||||
describe('Student controllers', () => {
|
describe('Student controllers', () => {
|
||||||
let req: Partial<Request>;
|
let req: Partial<Request>;
|
||||||
|
@ -41,7 +45,8 @@ describe('Student controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get student', async () => {
|
it('Get student', async () => {
|
||||||
req = { params: { username: 'DireStraits' } };
|
const student = getDireStraits();
|
||||||
|
req = { params: { username: student.username } };
|
||||||
|
|
||||||
await getStudentHandler(req as Request, res as Response);
|
await getStudentHandler(req as Request, res as Response);
|
||||||
|
|
||||||
|
@ -83,11 +88,12 @@ describe('Student controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Create duplicate student', async () => {
|
it('Create duplicate student', async () => {
|
||||||
|
const student = getDireStraits();
|
||||||
req = {
|
req = {
|
||||||
body: {
|
body: {
|
||||||
username: 'DireStraits',
|
username: student.username,
|
||||||
firstName: 'dupe',
|
firstName: student.firstName,
|
||||||
lastName: 'dupe',
|
lastName: student.lastName,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,14 +117,17 @@ describe('Student controllers', () => {
|
||||||
|
|
||||||
// Check is DireStraits is part of the student list
|
// Check is DireStraits is part of the student list
|
||||||
const studentUsernames = result.students.map((s: StudentDTO) => s.username);
|
const studentUsernames = result.students.map((s: StudentDTO) => s.username);
|
||||||
expect(studentUsernames).toContain('DireStraits');
|
|
||||||
|
expect(studentUsernames).toContain(TEST_STUDENTS[0].username);
|
||||||
|
|
||||||
// Check length, +1 because of create
|
// Check length, +1 because of create
|
||||||
expect(result.students).toHaveLength(TEST_STUDENTS.length);
|
expect(result.students).toHaveLength(TEST_STUDENTS.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Student classes', async () => {
|
it('Student classes', async () => {
|
||||||
req = { params: { username: 'DireStraits' }, query: {} };
|
const class_ = getClass02();
|
||||||
|
const member = class_.students[0];
|
||||||
|
req = { params: { username: member.username }, query: {} };
|
||||||
|
|
||||||
await getStudentClassesHandler(req as Request, res as Response);
|
await getStudentClassesHandler(req as Request, res as Response);
|
||||||
|
|
||||||
|
@ -129,7 +138,9 @@ describe('Student controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Student groups', async () => {
|
it('Student groups', async () => {
|
||||||
req = { params: { username: 'DireStraits' }, query: {} };
|
const group = getTestGroup01();
|
||||||
|
const member = group.members[0];
|
||||||
|
req = { params: { username: member.username }, query: {} };
|
||||||
|
|
||||||
await getStudentGroupsHandler(req as Request, res as Response);
|
await getStudentGroupsHandler(req as Request, res as Response);
|
||||||
|
|
||||||
|
@ -140,7 +151,8 @@ describe('Student controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Student submissions', async () => {
|
it('Student submissions', async () => {
|
||||||
req = { params: { username: 'DireStraits' }, query: { full: 'true' } };
|
const submission = getSubmission01();
|
||||||
|
req = { params: { username: submission.submitter.username }, query: { full: 'true' } };
|
||||||
|
|
||||||
await getStudentSubmissionsHandler(req as Request, res as Response);
|
await getStudentSubmissionsHandler(req as Request, res as Response);
|
||||||
|
|
||||||
|
@ -151,7 +163,8 @@ describe('Student controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Student questions', async () => {
|
it('Student questions', async () => {
|
||||||
req = { params: { username: 'DireStraits' }, query: { full: 'true' } };
|
const question = getQuestion01();
|
||||||
|
req = { params: { username: question.author.username }, query: { full: 'true' } };
|
||||||
|
|
||||||
await getStudentQuestionsHandler(req as Request, res as Response);
|
await getStudentQuestionsHandler(req as Request, res as Response);
|
||||||
|
|
||||||
|
@ -168,8 +181,9 @@ describe('Student controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get join requests by student', async () => {
|
it('Get join requests by student', async () => {
|
||||||
|
const jr = getClassJoinRequest02();
|
||||||
req = {
|
req = {
|
||||||
params: { username: 'PinkFloyd' },
|
params: { username: jr.requester.username },
|
||||||
};
|
};
|
||||||
|
|
||||||
await getStudentRequestsHandler(req as Request, res as Response);
|
await getStudentRequestsHandler(req as Request, res as Response);
|
||||||
|
@ -186,8 +200,9 @@ describe('Student controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get join request by student and class', async () => {
|
it('Get join request by student and class', async () => {
|
||||||
|
const jr = getClassJoinRequest02();
|
||||||
req = {
|
req = {
|
||||||
params: { username: 'PinkFloyd', classId: getClass02().classId },
|
params: { username: jr.requester.username, classId: jr.class.classId! },
|
||||||
};
|
};
|
||||||
|
|
||||||
await getStudentRequestHandler(req as Request, res as Response);
|
await getStudentRequestHandler(req as Request, res as Response);
|
||||||
|
@ -200,9 +215,11 @@ describe('Student controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Create and delete join request', async () => {
|
it('Create and delete join request', async () => {
|
||||||
|
const student = getTheDoors();
|
||||||
|
const class_ = getClass02();
|
||||||
req = {
|
req = {
|
||||||
params: { username: 'TheDoors' },
|
params: { username: student.username },
|
||||||
body: { classId: getClass02().classId },
|
body: { classId: class_.classId! },
|
||||||
};
|
};
|
||||||
|
|
||||||
await createStudentRequestHandler(req as Request, res as Response);
|
await createStudentRequestHandler(req as Request, res as Response);
|
||||||
|
@ -210,7 +227,7 @@ describe('Student controllers', () => {
|
||||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ request: expect.anything() }));
|
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ request: expect.anything() }));
|
||||||
|
|
||||||
req = {
|
req = {
|
||||||
params: { username: 'TheDoors', classId: getClass02().classId },
|
params: { username: student.username, classId: class_.classId! },
|
||||||
};
|
};
|
||||||
|
|
||||||
await deleteClassJoinRequestHandler(req as Request, res as Response);
|
await deleteClassJoinRequestHandler(req as Request, res as Response);
|
||||||
|
@ -221,18 +238,21 @@ describe('Student controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Create join request student already in class error', async () => {
|
it('Create join request student already in class error', async () => {
|
||||||
|
const student = getNoordkaap();
|
||||||
|
const class_ = getClass02();
|
||||||
req = {
|
req = {
|
||||||
params: { username: 'Noordkaap' },
|
params: { username: student.username },
|
||||||
body: { classId: getClass02().classId },
|
body: { classId: class_.classId! },
|
||||||
};
|
};
|
||||||
|
|
||||||
await expect(async () => createStudentRequestHandler(req as Request, res as Response)).rejects.toThrow(ConflictException);
|
await expect(async () => createStudentRequestHandler(req as Request, res as Response)).rejects.toThrow(ConflictException);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Create join request duplicate', async () => {
|
it('Create join request duplicate', async () => {
|
||||||
|
const jr = getClassJoinRequest02();
|
||||||
req = {
|
req = {
|
||||||
params: { username: 'Tool' },
|
params: { username: jr.requester.username },
|
||||||
body: { classId: getClass02().classId },
|
body: { classId: jr.class.classId! },
|
||||||
};
|
};
|
||||||
|
|
||||||
await expect(async () => createStudentRequestHandler(req as Request, res as Response)).rejects.toThrow(ConflictException);
|
await expect(async () => createStudentRequestHandler(req as Request, res as Response)).rejects.toThrow(ConflictException);
|
||||||
|
|
|
@ -12,7 +12,9 @@ import { TeacherInvitationData } from '@dwengo-1/common/interfaces/teacher-invit
|
||||||
import { getClassHandler } from '../../src/controllers/classes';
|
import { getClassHandler } from '../../src/controllers/classes';
|
||||||
import { BadRequestException } from '../../src/exceptions/bad-request-exception';
|
import { BadRequestException } from '../../src/exceptions/bad-request-exception';
|
||||||
import { ClassStatus } from '@dwengo-1/common/util/class-join-request';
|
import { ClassStatus } from '@dwengo-1/common/util/class-join-request';
|
||||||
import { getClass02 } from '../test_assets/classes/classes.testdata';
|
import { getTeacherInvitation01 } from '../test_assets/classes/teacher-invitations.testdata.js';
|
||||||
|
import { getLimpBizkit, getTestleerkracht1 } from '../test_assets/users/teachers.testdata.js';
|
||||||
|
import { getClass02 } from '../test_assets/classes/classes.testdata.js';
|
||||||
|
|
||||||
describe('Teacher controllers', () => {
|
describe('Teacher controllers', () => {
|
||||||
let req: Partial<Request>;
|
let req: Partial<Request>;
|
||||||
|
@ -32,7 +34,8 @@ describe('Teacher controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get teacher invitations by', async () => {
|
it('Get teacher invitations by', async () => {
|
||||||
req = { params: { username: 'LimpBizkit' }, query: { sent: 'true' } };
|
const ti = getTeacherInvitation01();
|
||||||
|
req = { params: { username: ti.sender.username }, query: { sent: 'true' } };
|
||||||
|
|
||||||
await getAllInvitationsHandler(req as Request, res as Response);
|
await getAllInvitationsHandler(req as Request, res as Response);
|
||||||
|
|
||||||
|
@ -44,7 +47,8 @@ describe('Teacher controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get teacher invitations for', async () => {
|
it('Get teacher invitations for', async () => {
|
||||||
req = { params: { username: 'FooFighters' }, query: { by: 'false' } };
|
const ti = getTeacherInvitation01();
|
||||||
|
req = { params: { username: ti.receiver.username }, query: { by: 'false' } };
|
||||||
|
|
||||||
await getAllInvitationsHandler(req as Request, res as Response);
|
await getAllInvitationsHandler(req as Request, res as Response);
|
||||||
|
|
||||||
|
@ -55,10 +59,13 @@ describe('Teacher controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Create and delete invitation', async () => {
|
it('Create and delete invitation', async () => {
|
||||||
|
const sender = getLimpBizkit();
|
||||||
|
const receiver = getTestleerkracht1();
|
||||||
|
const class_ = getClass02();
|
||||||
const body = {
|
const body = {
|
||||||
sender: 'LimpBizkit',
|
sender: sender.username,
|
||||||
receiver: 'testleerkracht1',
|
receiver: receiver.username,
|
||||||
class: getClass02().classId,
|
class: class_.classId,
|
||||||
} as TeacherInvitationData;
|
} as TeacherInvitationData;
|
||||||
req = { body };
|
req = { body };
|
||||||
|
|
||||||
|
@ -66,9 +73,9 @@ describe('Teacher controllers', () => {
|
||||||
|
|
||||||
req = {
|
req = {
|
||||||
params: {
|
params: {
|
||||||
sender: 'LimpBizkit',
|
sender: sender.username,
|
||||||
receiver: 'testleerkracht1',
|
receiver: receiver.username,
|
||||||
classId: getClass02().classId,
|
classId: class_.classId!,
|
||||||
},
|
},
|
||||||
body: { accepted: 'false' },
|
body: { accepted: 'false' },
|
||||||
};
|
};
|
||||||
|
@ -77,11 +84,12 @@ describe('Teacher controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get invitation', async () => {
|
it('Get invitation', async () => {
|
||||||
|
const ti = getTeacherInvitation01();
|
||||||
req = {
|
req = {
|
||||||
params: {
|
params: {
|
||||||
sender: 'LimpBizkit',
|
sender: ti.sender.username,
|
||||||
receiver: 'FooFighters',
|
receiver: ti.receiver.username,
|
||||||
classId: getClass02().classId,
|
classId: ti.class.classId!,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await getInvitationHandler(req as Request, res as Response);
|
await getInvitationHandler(req as Request, res as Response);
|
||||||
|
@ -98,10 +106,11 @@ describe('Teacher controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Accept invitation', async () => {
|
it('Accept invitation', async () => {
|
||||||
|
const ti = getTeacherInvitation01();
|
||||||
const body = {
|
const body = {
|
||||||
sender: 'LimpBizkit',
|
sender: ti.sender.username,
|
||||||
receiver: 'FooFighters',
|
receiver: ti.receiver.username,
|
||||||
class: getClass02().classId,
|
class: ti.class.classId,
|
||||||
} as TeacherInvitationData;
|
} as TeacherInvitationData;
|
||||||
req = { body };
|
req = { body };
|
||||||
|
|
||||||
|
@ -112,13 +121,13 @@ describe('Teacher controllers', () => {
|
||||||
|
|
||||||
req = {
|
req = {
|
||||||
params: {
|
params: {
|
||||||
id: getClass02().classId,
|
id: ti.class.classId!,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
await getClassHandler(req as Request, res as Response);
|
await getClassHandler(req as Request, res as Response);
|
||||||
|
|
||||||
const result = jsonMock.mock.lastCall?.[0];
|
const result = jsonMock.mock.lastCall?.[0];
|
||||||
expect(result.class.teachers).toContain('FooFighters');
|
expect(result.class.teachers).toContain(ti.receiver.username);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,7 +16,9 @@ import { BadRequestException } from '../../src/exceptions/bad-request-exception.
|
||||||
import { EntityAlreadyExistsException } from '../../src/exceptions/entity-already-exists-exception.js';
|
import { EntityAlreadyExistsException } from '../../src/exceptions/entity-already-exists-exception.js';
|
||||||
import { getStudentRequestsHandler } from '../../src/controllers/students.js';
|
import { getStudentRequestsHandler } from '../../src/controllers/students.js';
|
||||||
import { getClassHandler } from '../../src/controllers/classes';
|
import { getClassHandler } from '../../src/controllers/classes';
|
||||||
import { getClass02 } from '../test_assets/classes/classes.testdata';
|
import { getFooFighters, getTestleerkracht1 } from '../test_assets/users/teachers.testdata.js';
|
||||||
|
import { getClass02 } from '../test_assets/classes/classes.testdata.js';
|
||||||
|
import { getClassJoinRequest01 } from '../test_assets/classes/class-join-requests.testdata.js';
|
||||||
|
|
||||||
describe('Teacher controllers', () => {
|
describe('Teacher controllers', () => {
|
||||||
let req: Partial<Request>;
|
let req: Partial<Request>;
|
||||||
|
@ -36,7 +38,8 @@ describe('Teacher controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get teacher', async () => {
|
it('Get teacher', async () => {
|
||||||
req = { params: { username: 'FooFighters' } };
|
const teacher = getFooFighters();
|
||||||
|
req = { params: { username: teacher.username } };
|
||||||
|
|
||||||
await getTeacherHandler(req as Request, res as Response);
|
await getTeacherHandler(req as Request, res as Response);
|
||||||
|
|
||||||
|
@ -77,12 +80,13 @@ describe('Teacher controllers', () => {
|
||||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ teacher: expect.objectContaining(teacher) }));
|
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ teacher: expect.objectContaining(teacher) }));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Create duplicate student', async () => {
|
it('Create duplicate teacher', async () => {
|
||||||
|
const teacher = getFooFighters();
|
||||||
req = {
|
req = {
|
||||||
body: {
|
body: {
|
||||||
username: 'FooFighters',
|
username: teacher.username,
|
||||||
firstName: 'Dave',
|
firstName: teacher.firstName,
|
||||||
lastName: 'Grohl',
|
lastName: teacher.lastName,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,20 +108,22 @@ describe('Teacher controllers', () => {
|
||||||
|
|
||||||
const result = jsonMock.mock.lastCall?.[0];
|
const result = jsonMock.mock.lastCall?.[0];
|
||||||
|
|
||||||
expect(result.teachers).toContain('testleerkracht1');
|
const teacher = getTestleerkracht1();
|
||||||
|
expect(result.teachers).toContain(teacher.username);
|
||||||
|
|
||||||
expect(result.teachers).toHaveLength(5);
|
expect(result.teachers.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Deleting non-existent student', async () => {
|
it('Deleting non-existent teacher', async () => {
|
||||||
req = { params: { username: 'doesnotexist' } };
|
req = { params: { username: 'doesnotexist' } };
|
||||||
|
|
||||||
await expect(async () => deleteTeacherHandler(req as Request, res as Response)).rejects.toThrow(NotFoundException);
|
await expect(async () => deleteTeacherHandler(req as Request, res as Response)).rejects.toThrow(NotFoundException);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get teacher classes', async () => {
|
it('Get teacher classes', async () => {
|
||||||
|
const class_ = getClass02();
|
||||||
req = {
|
req = {
|
||||||
params: { username: 'testleerkracht1' },
|
params: { username: class_.teachers[0].username },
|
||||||
query: { full: 'true' },
|
query: { full: 'true' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -130,9 +136,10 @@ describe('Teacher controllers', () => {
|
||||||
expect(result.classes.length).toBeGreaterThan(0);
|
expect(result.classes.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Get teacher students', async () => {
|
it('Get teacher teachers', async () => {
|
||||||
|
const teacher = getTestleerkracht1();
|
||||||
req = {
|
req = {
|
||||||
params: { username: 'testleerkracht1' },
|
params: { username: teacher.username },
|
||||||
query: { full: 'true' },
|
query: { full: 'true' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -145,30 +152,10 @@ describe('Teacher controllers', () => {
|
||||||
expect(result.students.length).toBeGreaterThan(0);
|
expect(result.students.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
It('Get teacher questions', async () => {
|
|
||||||
req = {
|
|
||||||
params: { username: 'FooFighters' },
|
|
||||||
query: { full: 'true' },
|
|
||||||
};
|
|
||||||
|
|
||||||
await getTeacherQuestionHandler(req as Request, res as Response);
|
|
||||||
|
|
||||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ questions: expect.anything() }));
|
|
||||||
|
|
||||||
const result = jsonMock.mock.lastCall?.[0];
|
|
||||||
// console.log('[TEACHER QUESTIONS]', result.questions);
|
|
||||||
expect(result.questions.length).toBeGreaterThan(0);
|
|
||||||
|
|
||||||
// TODO fix
|
|
||||||
});
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
it('Get join requests by class', async () => {
|
it('Get join requests by class', async () => {
|
||||||
|
const jr = getClassJoinRequest01();
|
||||||
req = {
|
req = {
|
||||||
params: { classId: getClass02().classId },
|
params: { classId: jr.class.classId! },
|
||||||
};
|
};
|
||||||
|
|
||||||
await getStudentJoinRequestHandler(req as Request, res as Response);
|
await getStudentJoinRequestHandler(req as Request, res as Response);
|
||||||
|
@ -181,8 +168,9 @@ describe('Teacher controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Update join request status', async () => {
|
it('Update join request status', async () => {
|
||||||
|
const jr = getClassJoinRequest01();
|
||||||
req = {
|
req = {
|
||||||
params: { classId: getClass02().classId, studentUsername: 'PinkFloyd' },
|
params: { classId: jr.class.classId!, studentUsername: jr.requester.username },
|
||||||
body: { accepted: 'true' },
|
body: { accepted: 'true' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -191,7 +179,7 @@ describe('Teacher controllers', () => {
|
||||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ request: expect.anything() }));
|
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ request: expect.anything() }));
|
||||||
|
|
||||||
req = {
|
req = {
|
||||||
params: { username: 'PinkFloyd' },
|
params: { username: jr.requester.username },
|
||||||
};
|
};
|
||||||
|
|
||||||
await getStudentRequestsHandler(req as Request, res as Response);
|
await getStudentRequestsHandler(req as Request, res as Response);
|
||||||
|
@ -200,11 +188,11 @@ describe('Teacher controllers', () => {
|
||||||
expect(status).toBeTruthy();
|
expect(status).toBeTruthy();
|
||||||
|
|
||||||
req = {
|
req = {
|
||||||
params: { id: getClass02().classId },
|
params: { id: jr.class.classId! },
|
||||||
};
|
};
|
||||||
|
|
||||||
await getClassHandler(req as Request, res as Response);
|
await getClassHandler(req as Request, res as Response);
|
||||||
const students: string[] = jsonMock.mock.lastCall?.[0].class.students;
|
const students: string[] = jsonMock.mock.lastCall?.[0].class.students;
|
||||||
expect(students).contains('PinkFloyd');
|
expect(students).contains(jr.requester.username);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,49 +1,60 @@
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
import { setupTestApp } from '../../setup-tests';
|
import { setupTestApp } from '../../setup-tests';
|
||||||
import { AssignmentRepository } from '../../../src/data/assignments/assignment-repository';
|
import { AssignmentRepository } from '../../../src/data/assignments/assignment-repository';
|
||||||
import { getAssignmentRepository, getClassRepository } from '../../../src/data/repositories';
|
import { getAssignmentRepository } from '../../../src/data/repositories';
|
||||||
import { ClassRepository } from '../../../src/data/classes/class-repository';
|
|
||||||
import { getClass02 } from '../../test_assets/classes/classes.testdata';
|
import { getClass02 } from '../../test_assets/classes/classes.testdata';
|
||||||
|
import { getAssignment02, getAssignment03 } from '../../test_assets/assignments/assignments.testdata';
|
||||||
|
import { getTestleerkracht1 } from '../../test_assets/users/teachers.testdata';
|
||||||
|
|
||||||
describe('AssignmentRepository', () => {
|
describe('AssignmentRepository', () => {
|
||||||
let assignmentRepository: AssignmentRepository;
|
let assignmentRepository: AssignmentRepository;
|
||||||
let classRepository: ClassRepository;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await setupTestApp();
|
await setupTestApp();
|
||||||
assignmentRepository = getAssignmentRepository();
|
assignmentRepository = getAssignmentRepository();
|
||||||
classRepository = getClassRepository();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the requested assignment', async () => {
|
it('should return the requested assignment', async () => {
|
||||||
const class_ = await classRepository.findById(getClass02().classId);
|
const class_ = getClass02();
|
||||||
const assignment = await assignmentRepository.findByClassAndId(class_!, 21001);
|
const usedAssignment = getAssignment02();
|
||||||
|
const assignment = await assignmentRepository.findByClassAndId(class_, 21001);
|
||||||
|
|
||||||
expect(assignment).toBeTruthy();
|
expect(assignment).toBeTruthy();
|
||||||
expect(assignment!.title).toBe('tool');
|
expect(assignment!.description).toBe(usedAssignment.description);
|
||||||
|
expect(assignment!.id).toBe(usedAssignment.id);
|
||||||
|
expect(assignment!.learningPathHruid).toBe(usedAssignment.learningPathHruid);
|
||||||
|
expect(assignment!.within.classId).toBe(usedAssignment.within.classId);
|
||||||
|
expect(assignment!.title).toBe(usedAssignment.title);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return all assignments for a class', async () => {
|
it('should return all assignments for a class', async () => {
|
||||||
const class_ = await classRepository.findById(getClass02().classId);
|
const class_ = getClass02();
|
||||||
const assignments = await assignmentRepository.findAllAssignmentsInClass(class_!);
|
const usedAssignment = getAssignment02();
|
||||||
|
const assignments = await assignmentRepository.findAllAssignmentsInClass(class_);
|
||||||
|
|
||||||
expect(assignments).toBeTruthy();
|
expect(assignments).toBeTruthy();
|
||||||
expect(assignments).toHaveLength(1);
|
expect(assignments).toHaveLength(1);
|
||||||
expect(assignments[0].title).toBe('tool');
|
const assignment = assignments[0];
|
||||||
|
expect(assignment.description).toBe(usedAssignment.description);
|
||||||
|
expect(assignment.id).toBe(usedAssignment.id);
|
||||||
|
expect(assignment.learningPathHruid).toBe(usedAssignment.learningPathHruid);
|
||||||
|
expect(assignment.within.classId).toBe(usedAssignment.within.classId);
|
||||||
|
expect(assignment.title).toBe(usedAssignment.title);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find all by username of the responsible teacher', async () => {
|
it('should find all by username of the responsible teacher', async () => {
|
||||||
const result = await assignmentRepository.findAllByResponsibleTeacher('testleerkracht1');
|
const teacher = getTestleerkracht1();
|
||||||
|
const result = await assignmentRepository.findAllByResponsibleTeacher(teacher.username);
|
||||||
const resultIds = result.map((it) => it.id).sort((a, b) => (a ?? 0) - (b ?? 0));
|
const resultIds = result.map((it) => it.id).sort((a, b) => (a ?? 0) - (b ?? 0));
|
||||||
|
|
||||||
expect(resultIds).toEqual([21000, 21002, 21003, 21004]);
|
expect(resultIds).toEqual([21000, 21002, 21003, 21004]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not find removed assignment', async () => {
|
it('should not find removed assignment', async () => {
|
||||||
const class_ = await classRepository.findById('id01');
|
const deleted = getAssignment03();
|
||||||
await assignmentRepository.deleteByClassAndId(class_!, 3);
|
await assignmentRepository.deleteByClassAndId(deleted.within, deleted.id!);
|
||||||
|
|
||||||
const assignment = await assignmentRepository.findByClassAndId(class_!, 3);
|
const assignment = await assignmentRepository.findByClassAndId(deleted.within, deleted.id!);
|
||||||
|
|
||||||
expect(assignment).toBeNull();
|
expect(assignment).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,50 +1,56 @@
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
import { setupTestApp } from '../../setup-tests';
|
import { setupTestApp } from '../../setup-tests';
|
||||||
import { GroupRepository } from '../../../src/data/assignments/group-repository';
|
import { GroupRepository } from '../../../src/data/assignments/group-repository';
|
||||||
import { getAssignmentRepository, getClassRepository, getGroupRepository } from '../../../src/data/repositories';
|
import { getGroupRepository } from '../../../src/data/repositories';
|
||||||
import { AssignmentRepository } from '../../../src/data/assignments/assignment-repository';
|
import { getAssignment01, getAssignment02 } from '../../test_assets/assignments/assignments.testdata';
|
||||||
import { ClassRepository } from '../../../src/data/classes/class-repository';
|
import { getTestGroup01, getTestGroup02, getTestGroup03 } from '../../test_assets/assignments/groups.testdata';
|
||||||
import { getClass01, getClass02 } from '../../test_assets/classes/classes.testdata';
|
import { getDireStraits, getNoordkaap } from '../../test_assets/users/students.testdata';
|
||||||
|
|
||||||
describe('GroupRepository', () => {
|
describe('GroupRepository', () => {
|
||||||
let groupRepository: GroupRepository;
|
let groupRepository: GroupRepository;
|
||||||
let assignmentRepository: AssignmentRepository;
|
|
||||||
let classRepository: ClassRepository;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await setupTestApp();
|
await setupTestApp();
|
||||||
groupRepository = getGroupRepository();
|
groupRepository = getGroupRepository();
|
||||||
assignmentRepository = getAssignmentRepository();
|
|
||||||
classRepository = getClassRepository();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the requested group', async () => {
|
it('should return the requested group', async () => {
|
||||||
const id = getClass01().classId;
|
const assignment = getAssignment01();
|
||||||
const class_ = await classRepository.findById(id);
|
const usedGroup = getTestGroup01();
|
||||||
const assignment = await assignmentRepository.findByClassAndId(class_!, 21000);
|
const member1 = getNoordkaap();
|
||||||
|
const member2 = getDireStraits();
|
||||||
|
|
||||||
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment!, 21001);
|
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment, usedGroup.groupNumber!);
|
||||||
|
|
||||||
expect(group).toBeTruthy();
|
expect(group).toBeTruthy();
|
||||||
|
expect(group?.groupNumber).toBe(usedGroup.groupNumber);
|
||||||
|
expect(group!.members[0].username).toBeOneOf([member1.username, member2.username]);
|
||||||
|
expect(group!.members[1].username).toBeOneOf([member1.username, member2.username]);
|
||||||
|
expect(group!.assignment.id).toBe(usedGroup.assignment.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return all groups for assignment', async () => {
|
it('should return all groups for assignment', async () => {
|
||||||
const class_ = await classRepository.findById(getClass01().classId);
|
const assignment = getAssignment01();
|
||||||
const assignment = await assignmentRepository.findByClassAndId(class_!, 21000);
|
const gr1 = getTestGroup01();
|
||||||
|
const gr2 = getTestGroup02();
|
||||||
|
const gr3 = getTestGroup03();
|
||||||
|
|
||||||
const groups = await groupRepository.findAllGroupsForAssignment(assignment!);
|
const groups = await groupRepository.findAllGroupsForAssignment(assignment);
|
||||||
|
|
||||||
expect(groups).toBeTruthy();
|
expect(groups).toBeTruthy();
|
||||||
expect(groups).toHaveLength(3);
|
expect(groups).toHaveLength(3);
|
||||||
|
expect(groups[0].groupNumber).toBeOneOf([gr1.groupNumber, gr2.groupNumber, gr3.groupNumber]);
|
||||||
|
expect(groups[1].groupNumber).toBeOneOf([gr1.groupNumber, gr2.groupNumber, gr3.groupNumber]);
|
||||||
|
expect(groups[2].groupNumber).toBeOneOf([gr1.groupNumber, gr2.groupNumber, gr3.groupNumber]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not find removed group', async () => {
|
it('should not find removed group', async () => {
|
||||||
const class_ = await classRepository.findById(getClass02().classId);
|
const assignment = getAssignment02();
|
||||||
const assignment = await assignmentRepository.findByClassAndId(class_!, 21001);
|
const deleted = getTestGroup01();
|
||||||
|
|
||||||
await groupRepository.deleteByAssignmentAndGroupNumber(assignment!, 21001);
|
await groupRepository.deleteByAssignmentAndGroupNumber(assignment, deleted.groupNumber!);
|
||||||
|
|
||||||
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment!, 1);
|
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment, deleted.groupNumber!);
|
||||||
|
|
||||||
expect(group).toBeNull();
|
expect(group).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,81 +1,75 @@
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
import { setupTestApp } from '../../setup-tests';
|
import { setupTestApp } from '../../setup-tests';
|
||||||
import { SubmissionRepository } from '../../../src/data/assignments/submission-repository';
|
import { SubmissionRepository } from '../../../src/data/assignments/submission-repository';
|
||||||
import {
|
import { getSubmissionRepository } from '../../../src/data/repositories';
|
||||||
getAssignmentRepository,
|
|
||||||
getClassRepository,
|
|
||||||
getGroupRepository,
|
|
||||||
getStudentRepository,
|
|
||||||
getSubmissionRepository,
|
|
||||||
} from '../../../src/data/repositories';
|
|
||||||
import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier';
|
import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier';
|
||||||
import { Language } from '@dwengo-1/common/util/language';
|
|
||||||
import { StudentRepository } from '../../../src/data/users/student-repository';
|
|
||||||
import { GroupRepository } from '../../../src/data/assignments/group-repository';
|
|
||||||
import { AssignmentRepository } from '../../../src/data/assignments/assignment-repository';
|
|
||||||
import { ClassRepository } from '../../../src/data/classes/class-repository';
|
|
||||||
import { Submission } from '../../../src/entities/assignments/submission.entity';
|
import { Submission } from '../../../src/entities/assignments/submission.entity';
|
||||||
import { Class } from '../../../src/entities/classes/class.entity';
|
|
||||||
import { Assignment } from '../../../src/entities/assignments/assignment.entity';
|
|
||||||
import { testLearningObject01 } from '../../test_assets/content/learning-objects.testdata';
|
import { testLearningObject01 } from '../../test_assets/content/learning-objects.testdata';
|
||||||
import { getClass01 } from '../../test_assets/classes/classes.testdata';
|
import { getSubmission01, getSubmission02, getSubmission07, getSubmission08 } from '../../test_assets/assignments/submission.testdata';
|
||||||
|
import { getAssignment01 } from '../../test_assets/assignments/assignments.testdata';
|
||||||
|
import { getTestGroup02 } from '../../test_assets/assignments/groups.testdata';
|
||||||
|
|
||||||
describe('SubmissionRepository', () => {
|
describe('SubmissionRepository', () => {
|
||||||
let submissionRepository: SubmissionRepository;
|
let submissionRepository: SubmissionRepository;
|
||||||
let studentRepository: StudentRepository;
|
|
||||||
let groupRepository: GroupRepository;
|
|
||||||
let assignmentRepository: AssignmentRepository;
|
|
||||||
let classRepository: ClassRepository;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await setupTestApp();
|
await setupTestApp();
|
||||||
submissionRepository = getSubmissionRepository();
|
submissionRepository = getSubmissionRepository();
|
||||||
studentRepository = getStudentRepository();
|
|
||||||
groupRepository = getGroupRepository();
|
|
||||||
assignmentRepository = getAssignmentRepository();
|
|
||||||
classRepository = getClassRepository();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find the requested submission', async () => {
|
it('should find the requested submission', async () => {
|
||||||
const id = new LearningObjectIdentifier('id03', Language.English, 1);
|
const usedSubmission = getSubmission01();
|
||||||
const submission = await submissionRepository.findSubmissionByLearningObjectAndSubmissionNumber(id, 1);
|
const id = new LearningObjectIdentifier(
|
||||||
|
usedSubmission.learningObjectHruid,
|
||||||
|
usedSubmission.learningObjectLanguage,
|
||||||
|
usedSubmission.learningObjectVersion
|
||||||
|
);
|
||||||
|
const submission = await submissionRepository.findSubmissionByLearningObjectAndSubmissionNumber(id, usedSubmission.submissionNumber!);
|
||||||
|
|
||||||
expect(submission).toBeTruthy();
|
expect(submission).toBeTruthy();
|
||||||
expect(submission?.content).toBe('sub1');
|
expect(submission?.content).toBe(usedSubmission.content);
|
||||||
|
expect(submission?.submissionNumber).toBe(usedSubmission.submissionNumber);
|
||||||
|
expect(submission?.submitter.username).toBe(usedSubmission.submitter.username);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find the most recent submission for a student', async () => {
|
it('should find the most recent submission for a student', async () => {
|
||||||
const id = new LearningObjectIdentifier('id02', Language.English, 1);
|
const usedSubmission = getSubmission02();
|
||||||
const student = await studentRepository.findByUsername('Noordkaap');
|
const id = new LearningObjectIdentifier(
|
||||||
const submission = await submissionRepository.findMostRecentSubmissionForStudent(id, student!);
|
usedSubmission.learningObjectHruid,
|
||||||
|
usedSubmission.learningObjectLanguage,
|
||||||
|
usedSubmission.learningObjectVersion
|
||||||
|
);
|
||||||
|
|
||||||
|
const submission = await submissionRepository.findMostRecentSubmissionForStudent(id, usedSubmission.submitter);
|
||||||
|
|
||||||
expect(submission).toBeTruthy();
|
expect(submission).toBeTruthy();
|
||||||
expect(submission?.submissionTime.getDate()).toBe(25);
|
expect(submission?.submissionTime).toStrictEqual(usedSubmission.submissionTime);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find the most recent submission for a group', async () => {
|
it('should find the most recent submission for a group', async () => {
|
||||||
const id = new LearningObjectIdentifier('id03', Language.English, 1);
|
const usedSubmission = getSubmission02();
|
||||||
const class_ = await classRepository.findById(getClass01().classId);
|
const id = new LearningObjectIdentifier(
|
||||||
const assignment = await assignmentRepository.findByClassAndId(class_!, 21000);
|
usedSubmission.learningObjectHruid,
|
||||||
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment!, 21001);
|
usedSubmission.learningObjectLanguage,
|
||||||
const submission = await submissionRepository.findMostRecentSubmissionForGroup(id, group!);
|
usedSubmission.learningObjectVersion
|
||||||
|
);
|
||||||
|
|
||||||
|
const submission = await submissionRepository.findMostRecentSubmissionForGroup(id, usedSubmission.onBehalfOf);
|
||||||
|
|
||||||
expect(submission).toBeTruthy();
|
expect(submission).toBeTruthy();
|
||||||
expect(submission?.submissionTime.getDate()).toBe(25);
|
expect(submission?.submissionTime).toStrictEqual(usedSubmission.submissionTime);
|
||||||
});
|
});
|
||||||
|
|
||||||
let clazz: Class | null;
|
|
||||||
let assignment: Assignment | null;
|
|
||||||
let loId: LearningObjectIdentifier;
|
|
||||||
it('should find all submissions for a certain learning object and assignment', async () => {
|
it('should find all submissions for a certain learning object and assignment', async () => {
|
||||||
clazz = await classRepository.findById(getClass01().classId);
|
const usedSubmission = getSubmission08();
|
||||||
assignment = await assignmentRepository.findByClassAndId(clazz!, 21000);
|
const assignment = getAssignment01();
|
||||||
loId = {
|
|
||||||
hruid: 'id02',
|
const loId = {
|
||||||
language: Language.English,
|
hruid: usedSubmission.learningObjectHruid,
|
||||||
version: 1,
|
language: usedSubmission.learningObjectLanguage,
|
||||||
|
version: usedSubmission.learningObjectVersion,
|
||||||
};
|
};
|
||||||
const result = await submissionRepository.findAllSubmissionsForLearningObjectAndAssignment(loId, assignment!);
|
const result = await submissionRepository.findAllSubmissionsForLearningObjectAndAssignment(loId, assignment);
|
||||||
sortSubmissions(result);
|
sortSubmissions(result);
|
||||||
|
|
||||||
expect(result).toHaveLength(3);
|
expect(result).toHaveLength(3);
|
||||||
|
@ -94,8 +88,15 @@ describe('SubmissionRepository', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find only the submissions for a certain learning object and assignment made for the given group', async () => {
|
it('should find only the submissions for a certain learning object and assignment made for the given group', async () => {
|
||||||
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment!, 21002);
|
const group = getTestGroup02();
|
||||||
const result = await submissionRepository.findAllSubmissionsForLearningObjectAndGroup(loId, group!);
|
const usedSubmission = getSubmission08();
|
||||||
|
const loId = {
|
||||||
|
hruid: usedSubmission.learningObjectHruid,
|
||||||
|
language: usedSubmission.learningObjectLanguage,
|
||||||
|
version: usedSubmission.learningObjectVersion,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await submissionRepository.findAllSubmissionsForLearningObjectAndGroup(loId, group);
|
||||||
|
|
||||||
expect(result).toHaveLength(1);
|
expect(result).toHaveLength(1);
|
||||||
|
|
||||||
|
@ -108,10 +109,11 @@ describe('SubmissionRepository', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not find a deleted submission', async () => {
|
it('should not find a deleted submission', async () => {
|
||||||
|
const usedSubmission = getSubmission07();
|
||||||
const id = new LearningObjectIdentifier(testLearningObject01.hruid, testLearningObject01.language, testLearningObject01.version);
|
const id = new LearningObjectIdentifier(testLearningObject01.hruid, testLearningObject01.language, testLearningObject01.version);
|
||||||
await submissionRepository.deleteSubmissionByLearningObjectAndSubmissionNumber(id, 1);
|
await submissionRepository.deleteSubmissionByLearningObjectAndSubmissionNumber(id, usedSubmission.submissionNumber!);
|
||||||
|
|
||||||
const submission = await submissionRepository.findSubmissionByLearningObjectAndSubmissionNumber(id, 1);
|
const submission = await submissionRepository.findSubmissionByLearningObjectAndSubmissionNumber(id, usedSubmission.submissionNumber!);
|
||||||
|
|
||||||
expect(submission).toBeNull();
|
expect(submission).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,45 +1,49 @@
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
import { setupTestApp } from '../../setup-tests';
|
import { setupTestApp } from '../../setup-tests';
|
||||||
import { ClassJoinRequestRepository } from '../../../src/data/classes/class-join-request-repository';
|
import { ClassJoinRequestRepository } from '../../../src/data/classes/class-join-request-repository';
|
||||||
import { getClassJoinRequestRepository, getClassRepository, getStudentRepository } from '../../../src/data/repositories';
|
import { getClassJoinRequestRepository } from '../../../src/data/repositories';
|
||||||
import { StudentRepository } from '../../../src/data/users/student-repository';
|
import { getPinkFloyd, getSmashingPumpkins } from '../../test_assets/users/students.testdata';
|
||||||
import { ClassRepository } from '../../../src/data/classes/class-repository';
|
|
||||||
import { getClass02, getClass03 } from '../../test_assets/classes/classes.testdata';
|
import { getClass02, getClass03 } from '../../test_assets/classes/classes.testdata';
|
||||||
|
import { getClassJoinRequest01, getClassJoinRequest02, getClassJoinRequest03 } from '../../test_assets/classes/class-join-requests.testdata';
|
||||||
|
|
||||||
describe('ClassJoinRequestRepository', () => {
|
describe('ClassJoinRequestRepository', () => {
|
||||||
let classJoinRequestRepository: ClassJoinRequestRepository;
|
let classJoinRequestRepository: ClassJoinRequestRepository;
|
||||||
let studentRepository: StudentRepository;
|
|
||||||
let cassRepository: ClassRepository;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await setupTestApp();
|
await setupTestApp();
|
||||||
classJoinRequestRepository = getClassJoinRequestRepository();
|
classJoinRequestRepository = getClassJoinRequestRepository();
|
||||||
studentRepository = getStudentRepository();
|
|
||||||
cassRepository = getClassRepository();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should list all requests from student to join classes', async () => {
|
it('should list all requests from student to join classes', async () => {
|
||||||
const student = await studentRepository.findByUsername('PinkFloyd');
|
const studentUsed = getPinkFloyd();
|
||||||
const requests = await classJoinRequestRepository.findAllRequestsBy(student!);
|
const jr1 = getClassJoinRequest01();
|
||||||
|
const jr2 = getClassJoinRequest03();
|
||||||
|
const requests = await classJoinRequestRepository.findAllRequestsBy(studentUsed);
|
||||||
|
|
||||||
expect(requests).toBeTruthy();
|
expect(requests).toBeTruthy();
|
||||||
expect(requests).toHaveLength(2);
|
expect(requests).toHaveLength(2);
|
||||||
|
expect(requests[0].class.classId!).toBeOneOf([jr1.class.classId!, jr2.class.classId!]);
|
||||||
|
expect(requests[1].class.classId!).toBeOneOf([jr1.class.classId!, jr2.class.classId!]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should list all requests to a single class', async () => {
|
it('should list all requests to a single class', async () => {
|
||||||
const class_ = await cassRepository.findById(getClass02().classId);
|
const class_ = getClass02();
|
||||||
const requests = await classJoinRequestRepository.findAllOpenRequestsTo(class_!);
|
const jr1 = getClassJoinRequest01();
|
||||||
|
const jr2 = getClassJoinRequest02();
|
||||||
|
const requests = await classJoinRequestRepository.findAllOpenRequestsTo(class_);
|
||||||
|
|
||||||
expect(requests).toBeTruthy();
|
expect(requests).toBeTruthy();
|
||||||
expect(requests).toHaveLength(2);
|
expect(requests).toHaveLength(2);
|
||||||
|
expect(requests[0].class.classId).toBeOneOf([jr1.class.classId, jr2.class.classId]);
|
||||||
|
expect(requests[1].class.classId).toBeOneOf([jr1.class.classId, jr2.class.classId]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not find a removed request', async () => {
|
it('should not find a removed request', async () => {
|
||||||
const student = await studentRepository.findByUsername('SmashingPumpkins');
|
const studentUsed = getSmashingPumpkins();
|
||||||
const class_ = await cassRepository.findById(getClass03().classId);
|
const class_ = getClass03();
|
||||||
await classJoinRequestRepository.deleteBy(student!, class_!);
|
await classJoinRequestRepository.deleteBy(studentUsed, class_);
|
||||||
|
|
||||||
const request = await classJoinRequestRepository.findAllRequestsBy(student!);
|
const request = await classJoinRequestRepository.findAllRequestsBy(studentUsed);
|
||||||
|
|
||||||
expect(request).toHaveLength(0);
|
expect(request).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { ClassRepository } from '../../../src/data/classes/class-repository';
|
||||||
import { setupTestApp } from '../../setup-tests';
|
import { setupTestApp } from '../../setup-tests';
|
||||||
import { getClassRepository } from '../../../src/data/repositories';
|
import { getClassRepository } from '../../../src/data/repositories';
|
||||||
import { getClass01, getClass04 } from '../../test_assets/classes/classes.testdata';
|
import { getClass01, getClass04 } from '../../test_assets/classes/classes.testdata';
|
||||||
|
import { getClass01, getClass04 } from '../../test_assets/classes/classes.testdata';
|
||||||
|
|
||||||
describe('ClassRepository', () => {
|
describe('ClassRepository', () => {
|
||||||
let classRepository: ClassRepository;
|
let classRepository: ClassRepository;
|
||||||
|
@ -19,16 +20,18 @@ describe('ClassRepository', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return requested class', async () => {
|
it('should return requested class', async () => {
|
||||||
const classVar = await classRepository.findById(getClass01().classId);
|
const expected = getClass01();
|
||||||
|
const classVar = await classRepository.findById(expected.classId!);
|
||||||
|
|
||||||
expect(classVar).toBeTruthy();
|
expect(classVar).toBeTruthy();
|
||||||
expect(classVar?.displayName).toBe('class01');
|
expect(classVar?.displayName).toBe(expected.displayName);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('class should be gone after deletion', async () => {
|
it('class should be gone after deletion', async () => {
|
||||||
await classRepository.deleteById(getClass04().classId);
|
const deleted = getClass04();
|
||||||
|
await classRepository.deleteById(deleted.classId!);
|
||||||
|
|
||||||
const classVar = await classRepository.findById(getClass04().classId);
|
const classVar = await classRepository.findById(deleted.classId!);
|
||||||
|
|
||||||
expect(classVar).toBeNull();
|
expect(classVar).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,54 +1,62 @@
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
import { setupTestApp } from '../../setup-tests';
|
import { setupTestApp } from '../../setup-tests';
|
||||||
import { getClassRepository, getTeacherInvitationRepository, getTeacherRepository } from '../../../src/data/repositories';
|
import { getTeacherInvitationRepository } from '../../../src/data/repositories';
|
||||||
import { TeacherInvitationRepository } from '../../../src/data/classes/teacher-invitation-repository';
|
import { TeacherInvitationRepository } from '../../../src/data/classes/teacher-invitation-repository';
|
||||||
import { TeacherRepository } from '../../../src/data/users/teacher-repository';
|
import { getFooFighters, getLimpBizkit } from '../../test_assets/users/teachers.testdata';
|
||||||
import { ClassRepository } from '../../../src/data/classes/class-repository';
|
import { getTeacherInvitation01, getTeacherInvitation02, getTeacherInvitation03 } from '../../test_assets/classes/teacher-invitations.testdata';
|
||||||
import { getClass01, getClass02 } from '../../test_assets/classes/classes.testdata';
|
import { getClass01, getClass02 } from '../../test_assets/classes/classes.testdata';
|
||||||
|
|
||||||
describe('ClassRepository', () => {
|
describe('ClassRepository', () => {
|
||||||
let teacherInvitationRepository: TeacherInvitationRepository;
|
let teacherInvitationRepository: TeacherInvitationRepository;
|
||||||
let teacherRepository: TeacherRepository;
|
|
||||||
let classRepository: ClassRepository;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await setupTestApp();
|
await setupTestApp();
|
||||||
teacherInvitationRepository = getTeacherInvitationRepository();
|
teacherInvitationRepository = getTeacherInvitationRepository();
|
||||||
teacherRepository = getTeacherRepository();
|
|
||||||
classRepository = getClassRepository();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return all invitations from a teacher', async () => {
|
it('should return all invitations from a teacher', async () => {
|
||||||
const teacher = await teacherRepository.findByUsername('LimpBizkit');
|
const teacher = getLimpBizkit();
|
||||||
const invitations = await teacherInvitationRepository.findAllInvitationsBy(teacher!);
|
const ti1 = getTeacherInvitation01();
|
||||||
|
const ti2 = getTeacherInvitation02();
|
||||||
|
const invitations = await teacherInvitationRepository.findAllInvitationsBy(teacher);
|
||||||
|
|
||||||
expect(invitations).toBeTruthy();
|
expect(invitations).toBeTruthy();
|
||||||
expect(invitations).toHaveLength(2);
|
expect(invitations).toHaveLength(2);
|
||||||
|
expect(invitations[0].class.classId).toBeOneOf([ti1.class.classId, ti2.class.classId]);
|
||||||
|
expect(invitations[1].class.classId).toBeOneOf([ti1.class.classId, ti2.class.classId]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return all invitations for a teacher', async () => {
|
it('should return all invitations for a teacher', async () => {
|
||||||
const teacher = await teacherRepository.findByUsername('FooFighters');
|
const teacher = getFooFighters();
|
||||||
const invitations = await teacherInvitationRepository.findAllInvitationsFor(teacher!);
|
const ti1 = getTeacherInvitation01();
|
||||||
|
const ti2 = getTeacherInvitation03();
|
||||||
|
const invitations = await teacherInvitationRepository.findAllInvitationsFor(teacher);
|
||||||
|
|
||||||
expect(invitations).toBeTruthy();
|
expect(invitations).toBeTruthy();
|
||||||
expect(invitations).toHaveLength(2);
|
expect(invitations).toHaveLength(2);
|
||||||
|
expect(invitations[0].class.classId).toBeOneOf([ti1.class.classId, ti2.class.classId]);
|
||||||
|
expect(invitations[1].class.classId).toBeOneOf([ti1.class.classId, ti2.class.classId]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return all invitations for a class', async () => {
|
it('should return all invitations for a class', async () => {
|
||||||
const class_ = await classRepository.findById(getClass02().classId);
|
const class_ = getClass02();
|
||||||
const invitations = await teacherInvitationRepository.findAllInvitationsForClass(class_!);
|
const ti1 = getTeacherInvitation01();
|
||||||
|
const ti2 = getTeacherInvitation02();
|
||||||
|
const invitations = await teacherInvitationRepository.findAllInvitationsForClass(class_);
|
||||||
|
|
||||||
expect(invitations).toBeTruthy();
|
expect(invitations).toBeTruthy();
|
||||||
expect(invitations).toHaveLength(2);
|
expect(invitations).toHaveLength(2);
|
||||||
|
expect(invitations[0].class.classId).toBeOneOf([ti1.class.classId, ti2.class.classId]);
|
||||||
|
expect(invitations[1].class.classId).toBeOneOf([ti1.class.classId, ti2.class.classId]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not find a removed invitation', async () => {
|
it('should not find a removed invitation', async () => {
|
||||||
const class_ = await classRepository.findById(getClass01().classId);
|
const class_ = getClass01();
|
||||||
const sender = await teacherRepository.findByUsername('FooFighters');
|
const sender = getFooFighters();
|
||||||
const receiver = await teacherRepository.findByUsername('LimpBizkit');
|
const receiver = getLimpBizkit();
|
||||||
await teacherInvitationRepository.deleteBy(class_!, sender!, receiver!);
|
await teacherInvitationRepository.deleteBy(class_, sender, receiver);
|
||||||
|
|
||||||
const invitation = await teacherInvitationRepository.findAllInvitationsBy(sender!);
|
const invitation = await teacherInvitationRepository.findAllInvitationsBy(sender);
|
||||||
|
|
||||||
expect(invitation).toHaveLength(0);
|
expect(invitation).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
import { setupTestApp } from '../../setup-tests.js';
|
import { setupTestApp } from '../../setup-tests.js';
|
||||||
import { getAttachmentRepository } from '../../../src/data/repositories.js';
|
import { getAttachmentRepository } from '../../../src/data/repositories.js';
|
||||||
import { AttachmentRepository } from '../../../src/data/content/attachment-repository.js';
|
import { AttachmentRepository } from '../../../src/data/content/attachment-repository.js';
|
||||||
import { testLearningObject02 } from '../../test_assets/content/learning-objects.testdata';
|
import { getAttachment01 } from '../../test_assets/content/attachments.testdata.js';
|
||||||
|
|
||||||
describe('AttachmentRepository', () => {
|
describe('AttachmentRepository', () => {
|
||||||
let attachmentRepository: AttachmentRepository;
|
let attachmentRepository: AttachmentRepository;
|
||||||
|
@ -13,10 +13,11 @@ describe('AttachmentRepository', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the requested attachment', async () => {
|
it('should return the requested attachment', async () => {
|
||||||
|
const usedAttachment = getAttachment01();
|
||||||
const attachment = await attachmentRepository.findByMostRecentVersionOfLearningObjectAndName(
|
const attachment = await attachmentRepository.findByMostRecentVersionOfLearningObjectAndName(
|
||||||
testLearningObject02.hruid,
|
usedAttachment.learningObject.hruid,
|
||||||
testLearningObject02.language,
|
usedAttachment.learningObject.language,
|
||||||
'attachment01'
|
usedAttachment.name
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(attachment).toBeTruthy();
|
expect(attachment).toBeTruthy();
|
||||||
|
|
|
@ -37,7 +37,8 @@ describe('LearningObjectRepository', () => {
|
||||||
let newerExample: LearningObject;
|
let newerExample: LearningObject;
|
||||||
|
|
||||||
it('should allow a learning object with the same id except a different version to be added', async () => {
|
it('should allow a learning object with the same id except a different version to be added', async () => {
|
||||||
const testLearningObject01Newer = structuredClone(testLearningObject01);
|
// StructeredClone failed on teacher, this copies all fields to a json object
|
||||||
|
const testLearningObject01Newer = { ...testLearningObject01 };
|
||||||
testLearningObject01Newer.version = 10;
|
testLearningObject01Newer.version = 10;
|
||||||
testLearningObject01Newer.title += ' (nieuw)';
|
testLearningObject01Newer.title += ' (nieuw)';
|
||||||
testLearningObject01Newer.uuid = v4();
|
testLearningObject01Newer.uuid = v4();
|
||||||
|
|
|
@ -1,48 +1,39 @@
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
import { setupTestApp } from '../../setup-tests';
|
import { setupTestApp } from '../../setup-tests';
|
||||||
import { AnswerRepository } from '../../../src/data/questions/answer-repository';
|
import { AnswerRepository } from '../../../src/data/questions/answer-repository';
|
||||||
import { getAnswerRepository, getQuestionRepository, getTeacherRepository } from '../../../src/data/repositories';
|
import { getAnswerRepository } from '../../../src/data/repositories';
|
||||||
import { QuestionRepository } from '../../../src/data/questions/question-repository';
|
import { getQuestion01, getQuestion02 } from '../../test_assets/questions/questions.testdata';
|
||||||
import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier';
|
import { getAnswer01, getAnswer02, getAnswer03 } from '../../test_assets/questions/answers.testdata';
|
||||||
import { Language } from '@dwengo-1/common/util/language';
|
import { getFooFighters } from '../../test_assets/users/teachers.testdata';
|
||||||
import { TeacherRepository } from '../../../src/data/users/teacher-repository';
|
|
||||||
|
|
||||||
describe('AnswerRepository', () => {
|
describe('AnswerRepository', () => {
|
||||||
let answerRepository: AnswerRepository;
|
let answerRepository: AnswerRepository;
|
||||||
let questionRepository: QuestionRepository;
|
|
||||||
let teacherRepository: TeacherRepository;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await setupTestApp();
|
await setupTestApp();
|
||||||
answerRepository = getAnswerRepository();
|
answerRepository = getAnswerRepository();
|
||||||
questionRepository = getQuestionRepository();
|
|
||||||
teacherRepository = getTeacherRepository();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find all answers to a question', async () => {
|
it('should find all answers to a question', async () => {
|
||||||
const id = new LearningObjectIdentifier('id05', Language.English, 1);
|
const question = getQuestion02();
|
||||||
const questions = await questionRepository.findAllQuestionsAboutLearningObject(id);
|
const a1 = getAnswer01();
|
||||||
|
const a2 = getAnswer02();
|
||||||
|
|
||||||
const question = questions.find((it) => it.sequenceNumber === 2);
|
const answers = await answerRepository.findAllAnswersToQuestion(question);
|
||||||
|
|
||||||
const answers = await answerRepository.findAllAnswersToQuestion(question!);
|
|
||||||
|
|
||||||
expect(answers).toBeTruthy();
|
expect(answers).toBeTruthy();
|
||||||
expect(answers).toHaveLength(2);
|
expect(answers).toHaveLength(2);
|
||||||
expect(answers[0].content).toBeOneOf(['answer', 'answer2']);
|
expect(answers[0].content).toBeOneOf([a1.content, a2.content]);
|
||||||
expect(answers[1].content).toBeOneOf(['answer', 'answer2']);
|
expect(answers[1].content).toBeOneOf([a1.content, a2.content]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create an answer to a question', async () => {
|
it('should create an answer to a question', async () => {
|
||||||
const teacher = await teacherRepository.findByUsername('FooFighters');
|
const teacher = getFooFighters();
|
||||||
const id = new LearningObjectIdentifier('id05', Language.English, 1);
|
const question = getQuestion01();
|
||||||
const questions = await questionRepository.findAllQuestionsAboutLearningObject(id);
|
|
||||||
|
|
||||||
const question = questions[0];
|
|
||||||
|
|
||||||
await answerRepository.createAnswer({
|
await answerRepository.createAnswer({
|
||||||
toQuestion: question,
|
toQuestion: question,
|
||||||
author: teacher!,
|
author: teacher,
|
||||||
content: 'created answer',
|
content: 'created answer',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -54,12 +45,11 @@ describe('AnswerRepository', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not find a removed answer', async () => {
|
it('should not find a removed answer', async () => {
|
||||||
const id = new LearningObjectIdentifier('id04', Language.English, 1);
|
const deleted = getAnswer03();
|
||||||
const questions = await questionRepository.findAllQuestionsAboutLearningObject(id);
|
|
||||||
|
|
||||||
await answerRepository.removeAnswerByQuestionAndSequenceNumber(questions[0], 1);
|
await answerRepository.removeAnswerByQuestionAndSequenceNumber(deleted.toQuestion, deleted.sequenceNumber!);
|
||||||
|
|
||||||
const emptyList = await answerRepository.findAllAnswersToQuestion(questions[0]);
|
const emptyList = await answerRepository.findAllAnswersToQuestion(deleted.toQuestion);
|
||||||
|
|
||||||
expect(emptyList).toHaveLength(0);
|
expect(emptyList).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,70 +1,72 @@
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
import { setupTestApp } from '../../setup-tests';
|
import { setupTestApp } from '../../setup-tests';
|
||||||
import { QuestionRepository } from '../../../src/data/questions/question-repository';
|
import { QuestionRepository } from '../../../src/data/questions/question-repository';
|
||||||
import {
|
import { getQuestionRepository } from '../../../src/data/repositories';
|
||||||
getAssignmentRepository,
|
|
||||||
getClassRepository,
|
|
||||||
getGroupRepository,
|
|
||||||
getQuestionRepository,
|
|
||||||
getStudentRepository,
|
|
||||||
} from '../../../src/data/repositories';
|
|
||||||
import { StudentRepository } from '../../../src/data/users/student-repository';
|
|
||||||
import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier';
|
import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier';
|
||||||
import { Language } from '@dwengo-1/common/util/language';
|
|
||||||
import { Question } from '../../../src/entities/questions/question.entity';
|
import { Question } from '../../../src/entities/questions/question.entity';
|
||||||
import { Class } from '../../../src/entities/classes/class.entity';
|
import { testLearningObject03, testLearningObject05 } from '../../test_assets/content/learning-objects.testdata';
|
||||||
import { Assignment } from '../../../src/entities/assignments/assignment.entity';
|
import { getQuestion01, getQuestion02, getQuestion03, getQuestion05, getQuestion06 } from '../../test_assets/questions/questions.testdata';
|
||||||
import { getClass01 } from '../../test_assets/classes/classes.testdata';
|
import { getNoordkaap, getTool } from '../../test_assets/users/students.testdata';
|
||||||
|
import { getAssignment01 } from '../../test_assets/assignments/assignments.testdata';
|
||||||
|
import { getTestGroup01 } from '../../test_assets/assignments/groups.testdata';
|
||||||
|
|
||||||
describe('QuestionRepository', () => {
|
describe('QuestionRepository', () => {
|
||||||
let questionRepository: QuestionRepository;
|
let questionRepository: QuestionRepository;
|
||||||
let studentRepository: StudentRepository;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await setupTestApp();
|
await setupTestApp();
|
||||||
questionRepository = getQuestionRepository();
|
questionRepository = getQuestionRepository();
|
||||||
studentRepository = getStudentRepository();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return all questions part of the given learning object', async () => {
|
it('should return all questions part of the given learning object', async () => {
|
||||||
const id = new LearningObjectIdentifier('id05', Language.English, 1);
|
const q1 = getQuestion01();
|
||||||
const questions = await questionRepository.findAllQuestionsAboutLearningObject(id);
|
const q2 = getQuestion02();
|
||||||
|
const q3 = getQuestion05();
|
||||||
|
const q4 = getQuestion06();
|
||||||
|
const loid = {
|
||||||
|
hruid: q1.learningObjectHruid,
|
||||||
|
language: q1.learningObjectLanguage,
|
||||||
|
version: q1.learningObjectVersion,
|
||||||
|
} as LearningObjectIdentifier;
|
||||||
|
const questions = await questionRepository.findAllQuestionsAboutLearningObject(loid);
|
||||||
|
|
||||||
expect(questions).toBeTruthy();
|
expect(questions).toBeTruthy();
|
||||||
expect(questions).toHaveLength(4);
|
expect(questions).toHaveLength(4);
|
||||||
|
expect(questions[0].sequenceNumber!).toBeOneOf([q1.sequenceNumber, q2.sequenceNumber, q3.sequenceNumber, q4.sequenceNumber]);
|
||||||
|
expect(questions[1].sequenceNumber!).toBeOneOf([q1.sequenceNumber, q2.sequenceNumber, q3.sequenceNumber, q4.sequenceNumber]);
|
||||||
|
expect(questions[2].sequenceNumber!).toBeOneOf([q1.sequenceNumber, q2.sequenceNumber, q3.sequenceNumber, q4.sequenceNumber]);
|
||||||
|
expect(questions[3].sequenceNumber!).toBeOneOf([q1.sequenceNumber, q2.sequenceNumber, q3.sequenceNumber, q4.sequenceNumber]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create new question', async () => {
|
it('should create new question', async () => {
|
||||||
const id = new LearningObjectIdentifier('id03', Language.English, 1);
|
const id = {
|
||||||
const student = await studentRepository.findByUsername('Noordkaap');
|
hruid: testLearningObject03.hruid,
|
||||||
|
language: testLearningObject03.language,
|
||||||
const clazz = await getClassRepository().findById(getClass01().classId);
|
version: testLearningObject03.version,
|
||||||
const assignment = await getAssignmentRepository().findByClassAndId(clazz!, 21000);
|
} as LearningObjectIdentifier;
|
||||||
const group = await getGroupRepository().findByAssignmentAndGroupNumber(assignment!, 21001);
|
const student = getNoordkaap();
|
||||||
|
const group = getTestGroup01();
|
||||||
await questionRepository.createQuestion({
|
await questionRepository.createQuestion({
|
||||||
loId: id,
|
loId: id,
|
||||||
inGroup: group!,
|
inGroup: group,
|
||||||
author: student!,
|
author: student,
|
||||||
content: 'question?',
|
content: 'question?',
|
||||||
});
|
});
|
||||||
const question = await questionRepository.findAllQuestionsAboutLearningObject(id);
|
const question = await questionRepository.findAllQuestionsAboutLearningObject(id);
|
||||||
|
|
||||||
expect(question).toBeTruthy();
|
expect(question).toBeTruthy();
|
||||||
expect(question).toHaveLength(1);
|
expect(question).toHaveLength(1);
|
||||||
|
expect(question[0].content).toBe('question?');
|
||||||
});
|
});
|
||||||
|
|
||||||
let clazz: Class | null;
|
|
||||||
let assignment: Assignment | null;
|
|
||||||
let loId: LearningObjectIdentifier;
|
|
||||||
it('should find all questions for a certain learning object and assignment', async () => {
|
it('should find all questions for a certain learning object and assignment', async () => {
|
||||||
clazz = await getClassRepository().findById(getClass01().classId);
|
const assignment = getAssignment01();
|
||||||
assignment = await getAssignmentRepository().findByClassAndId(clazz!, 21000);
|
const loId = {
|
||||||
loId = {
|
hruid: testLearningObject05.hruid,
|
||||||
hruid: 'id05',
|
language: testLearningObject05.language,
|
||||||
language: Language.English,
|
version: testLearningObject05.version,
|
||||||
version: 1,
|
|
||||||
};
|
};
|
||||||
const result = await questionRepository.findAllQuestionsAboutLearningObjectInAssignment(loId, assignment!);
|
const result = await questionRepository.findAllQuestionsAboutLearningObjectInAssignment(loId, assignment);
|
||||||
sortQuestions(result);
|
sortQuestions(result);
|
||||||
|
|
||||||
expect(result).toHaveLength(3);
|
expect(result).toHaveLength(3);
|
||||||
|
@ -85,7 +87,14 @@ describe('QuestionRepository', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should find only the questions for a certain learning object and assignment asked by the user's group", async () => {
|
it("should find only the questions for a certain learning object and assignment asked by the user's group", async () => {
|
||||||
const result = await questionRepository.findAllQuestionsAboutLearningObjectInAssignment(loId, assignment!, 'Tool');
|
const loId = {
|
||||||
|
hruid: testLearningObject05.hruid,
|
||||||
|
language: testLearningObject05.language,
|
||||||
|
version: testLearningObject05.version,
|
||||||
|
};
|
||||||
|
const assignment = getAssignment01();
|
||||||
|
|
||||||
|
const result = await questionRepository.findAllQuestionsAboutLearningObjectInAssignment(loId, assignment, getTool().username);
|
||||||
// (student Tool is in group #2)
|
// (student Tool is in group #2)
|
||||||
|
|
||||||
expect(result).toHaveLength(1);
|
expect(result).toHaveLength(1);
|
||||||
|
@ -100,12 +109,17 @@ describe('QuestionRepository', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not find removed question', async () => {
|
it('should not find removed question', async () => {
|
||||||
const id = new LearningObjectIdentifier('id04', Language.English, 1);
|
const usedQuestion = getQuestion03();
|
||||||
await questionRepository.removeQuestionByLearningObjectAndSequenceNumber(id, 1);
|
const id = {
|
||||||
|
hruid: usedQuestion.learningObjectHruid,
|
||||||
|
language: usedQuestion.learningObjectLanguage,
|
||||||
|
version: usedQuestion.learningObjectVersion,
|
||||||
|
} as LearningObjectIdentifier;
|
||||||
|
await questionRepository.removeQuestionByLearningObjectAndSequenceNumber(id, usedQuestion.sequenceNumber!);
|
||||||
|
|
||||||
const question = await questionRepository.findAllQuestionsAboutLearningObject(id);
|
const questions = await questionRepository.findAllQuestionsAboutLearningObject(id);
|
||||||
|
|
||||||
expect(question).toHaveLength(0);
|
expect(questions).toHaveLength(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { setupTestApp } from '../../setup-tests.js';
|
||||||
import { describe, it, expect, beforeAll } from 'vitest';
|
import { describe, it, expect, beforeAll } from 'vitest';
|
||||||
import { StudentRepository } from '../../../src/data/users/student-repository.js';
|
import { StudentRepository } from '../../../src/data/users/student-repository.js';
|
||||||
import { getStudentRepository } from '../../../src/data/repositories.js';
|
import { getStudentRepository } from '../../../src/data/repositories.js';
|
||||||
|
import { getNoordkaap } from '../../test_assets/users/students.testdata.js';
|
||||||
|
|
||||||
const username = 'teststudent';
|
const username = 'teststudent';
|
||||||
const firstName = 'John';
|
const firstName = 'John';
|
||||||
|
@ -21,11 +22,12 @@ describe('StudentRepository', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return student from the datbase', async () => {
|
it('should return student from the datbase', async () => {
|
||||||
const student = await studentRepository.findByUsername('Noordkaap');
|
const expectation = getNoordkaap();
|
||||||
|
const student = await studentRepository.findByUsername(expectation.username);
|
||||||
|
|
||||||
expect(student).toBeTruthy();
|
expect(student).toBeTruthy();
|
||||||
expect(student?.firstName).toBe('Stijn');
|
expect(student?.firstName).toBe(expectation.firstName);
|
||||||
expect(student?.lastName).toBe('Meuris');
|
expect(student?.lastName).toBe(expectation.lastName);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the queried student after he was added', async () => {
|
it('should return the queried student after he was added', async () => {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { describe, it, expect, beforeAll } from 'vitest';
|
||||||
import { TeacherRepository } from '../../../src/data/users/teacher-repository';
|
import { TeacherRepository } from '../../../src/data/users/teacher-repository';
|
||||||
import { setupTestApp } from '../../setup-tests';
|
import { setupTestApp } from '../../setup-tests';
|
||||||
import { getTeacherRepository } from '../../../src/data/repositories';
|
import { getTeacherRepository } from '../../../src/data/repositories';
|
||||||
|
import { getFooFighters } from '../../test_assets/users/teachers.testdata';
|
||||||
|
|
||||||
const username = 'testteacher';
|
const username = 'testteacher';
|
||||||
const firstName = 'John';
|
const firstName = 'John';
|
||||||
|
@ -21,11 +22,12 @@ describe('TeacherRepository', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return teacher from the datbase', async () => {
|
it('should return teacher from the datbase', async () => {
|
||||||
const teacher = await teacherRepository.findByUsername('FooFighters');
|
const expected = getFooFighters();
|
||||||
|
const teacher = await teacherRepository.findByUsername(expected.username);
|
||||||
|
|
||||||
expect(teacher).toBeTruthy();
|
expect(teacher).toBeTruthy();
|
||||||
expect(teacher?.firstName).toBe('Dave');
|
expect(teacher?.firstName).toBe(expected.firstName);
|
||||||
expect(teacher?.lastName).toBe('Grohl');
|
expect(teacher?.lastName).toBe(expected.lastName);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the queried teacher after he was added', async () => {
|
it('should return the queried teacher after he was added', async () => {
|
||||||
|
@ -38,9 +40,9 @@ describe('TeacherRepository', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should no longer return the queried teacher after he was removed again', async () => {
|
it('should no longer return the queried teacher after he was removed again', async () => {
|
||||||
await teacherRepository.deleteByUsername('ZesdeMetaal');
|
await teacherRepository.deleteByUsername(username);
|
||||||
|
|
||||||
const retrievedTeacher = await teacherRepository.findByUsername('ZesdeMetaal');
|
const retrievedTeacher = await teacherRepository.findByUsername(username);
|
||||||
expect(retrievedTeacher).toBeNull();
|
expect(retrievedTeacher).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,11 +2,11 @@ import { forkEntityManager, initORM } from '../src/orm.js';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
import { makeTestStudents } from './test_assets/users/students.testdata.js';
|
import { makeTestStudents } from './test_assets/users/students.testdata.js';
|
||||||
import { makeTestTeachers } from './test_assets/users/teachers.testdata.js';
|
import { makeTestTeachers } from './test_assets/users/teachers.testdata.js';
|
||||||
import { makeTestLearningObjects } from './test_assets/content/learning-objects.testdata.js';
|
import { makeTestLearningObjects, testLearningObject01 } from './test_assets/content/learning-objects.testdata.js';
|
||||||
import { makeTestLearningPaths } from './test_assets/content/learning-paths.testdata.js';
|
import { makeTestLearningPaths } from './test_assets/content/learning-paths.testdata.js';
|
||||||
import { makeTestClasses } from './test_assets/classes/classes.testdata.js';
|
import { makeTestClasses } from './test_assets/classes/classes.testdata.js';
|
||||||
import { makeTestAssignemnts } from './test_assets/assignments/assignments.testdata.js';
|
import { getAssignment01, getAssignment02, makeTestAssignemnts } from './test_assets/assignments/assignments.testdata.js';
|
||||||
import { makeTestGroups } from './test_assets/assignments/groups.testdata.js';
|
import { getTestGroup01, getTestGroup02, getTestGroup03, getTestGroup04, makeTestGroups } from './test_assets/assignments/groups.testdata.js';
|
||||||
import { makeTestTeacherInvitations } from './test_assets/classes/teacher-invitations.testdata.js';
|
import { makeTestTeacherInvitations } from './test_assets/classes/teacher-invitations.testdata.js';
|
||||||
import { makeTestClassJoinRequests } from './test_assets/classes/class-join-requests.testdata.js';
|
import { makeTestClassJoinRequests } from './test_assets/classes/class-join-requests.testdata.js';
|
||||||
import { makeTestAttachments } from './test_assets/content/attachments.testdata.js';
|
import { makeTestAttachments } from './test_assets/content/attachments.testdata.js';
|
||||||
|
@ -26,22 +26,26 @@ export async function setupTestApp(): Promise<void> {
|
||||||
const teachers = makeTestTeachers(em);
|
const teachers = makeTestTeachers(em);
|
||||||
const learningObjects = makeTestLearningObjects(em);
|
const learningObjects = makeTestLearningObjects(em);
|
||||||
const learningPaths = makeTestLearningPaths(em);
|
const learningPaths = makeTestLearningPaths(em);
|
||||||
const classes = makeTestClasses(em, students, teachers);
|
const classes = makeTestClasses(em);
|
||||||
const assignments = makeTestAssignemnts(em, classes);
|
const assignments = makeTestAssignemnts(em);
|
||||||
const groups = makeTestGroups(em, students, assignments);
|
const groups = makeTestGroups(em);
|
||||||
|
|
||||||
assignments[0].groups = new Collection<Group>(groups.slice(0, 3));
|
const groups1 = [getTestGroup01(), getTestGroup02(), getTestGroup03()];
|
||||||
assignments[1].groups = new Collection<Group>(groups.slice(3, 4));
|
const groups2 = [getTestGroup04()];
|
||||||
|
const assignment1 = getAssignment01();
|
||||||
|
const assignment2 = getAssignment02();
|
||||||
|
assignment1.groups = new Collection<Group>(groups1);
|
||||||
|
assignment2.groups = new Collection<Group>(groups2);
|
||||||
|
|
||||||
const teacherInvitations = makeTestTeacherInvitations(em, teachers, classes);
|
const teacherInvitations = makeTestTeacherInvitations(em);
|
||||||
const classJoinRequests = makeTestClassJoinRequests(em, students, classes);
|
const classJoinRequests = makeTestClassJoinRequests(em);
|
||||||
const attachments = makeTestAttachments(em, learningObjects);
|
const attachments = makeTestAttachments(em);
|
||||||
|
|
||||||
learningObjects[1].attachments = attachments;
|
testLearningObject01.attachments = attachments;
|
||||||
|
|
||||||
const questions = makeTestQuestions(em, students, groups);
|
const questions = makeTestQuestions(em);
|
||||||
const answers = makeTestAnswers(em, teachers, questions);
|
const answers = makeTestAnswers(em);
|
||||||
const submissions = makeTestSubmissions(em, students, groups);
|
const submissions = makeTestSubmissions(em);
|
||||||
|
|
||||||
await em.persistAndFlush([
|
await em.persistAndFlush([
|
||||||
...students,
|
...students,
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { EntityManager } from '@mikro-orm/core';
|
import { EntityManager } from '@mikro-orm/core';
|
||||||
import { Assignment } from '../../../src/entities/assignments/assignment.entity';
|
import { Assignment } from '../../../src/entities/assignments/assignment.entity';
|
||||||
import { Class } from '../../../src/entities/classes/class.entity';
|
|
||||||
import { Language } from '@dwengo-1/common/util/language';
|
import { Language } from '@dwengo-1/common/util/language';
|
||||||
import { testLearningPathWithConditions } from '../content/learning-paths.testdata';
|
import { testLearningPath01, testLearningPath02, testLearningPathWithConditions } from '../content/learning-paths.testdata';
|
||||||
import { getClassWithTestleerlingAndTestleerkracht } from '../classes/classes.testdata';
|
import { getClass01, getClass02, getClassWithTestleerlingAndTestleerkracht } from '../classes/classes.testdata';
|
||||||
|
|
||||||
export function makeTestAssignemnts(em: EntityManager, classes: Class[]): Assignment[] {
|
export function makeTestAssignemnts(em: EntityManager): Assignment[] {
|
||||||
const futureDate = new Date();
|
const futureDate = new Date();
|
||||||
futureDate.setDate(futureDate.getDate() + 7);
|
futureDate.setDate(futureDate.getDate() + 7);
|
||||||
const pastDate = new Date();
|
const pastDate = new Date();
|
||||||
|
@ -14,44 +13,44 @@ export function makeTestAssignemnts(em: EntityManager, classes: Class[]): Assign
|
||||||
today.setHours(23, 59);
|
today.setHours(23, 59);
|
||||||
assignment01 = em.create(Assignment, {
|
assignment01 = em.create(Assignment, {
|
||||||
id: 21000,
|
id: 21000,
|
||||||
within: classes[0],
|
within: getClass01(),
|
||||||
title: 'dire straits',
|
title: 'dire straits',
|
||||||
description: 'reading',
|
description: 'reading',
|
||||||
learningPathHruid: 'un_ai',
|
learningPathHruid: testLearningPath02.hruid,
|
||||||
learningPathLanguage: Language.English,
|
learningPathLanguage: testLearningPath02.language as Language,
|
||||||
deadline: today,
|
deadline: today,
|
||||||
groups: [],
|
groups: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
assignment02 = em.create(Assignment, {
|
assignment02 = em.create(Assignment, {
|
||||||
id: 21001,
|
id: 21001,
|
||||||
within: classes[1],
|
within: getClass02(),
|
||||||
title: 'tool',
|
title: 'tool',
|
||||||
description: 'reading',
|
description: 'reading',
|
||||||
learningPathHruid: 'id01',
|
learningPathHruid: testLearningPath01.hruid,
|
||||||
learningPathLanguage: Language.English,
|
learningPathLanguage: testLearningPath01.language as Language,
|
||||||
deadline: futureDate,
|
deadline: futureDate,
|
||||||
groups: [],
|
groups: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
assignment03 = em.create(Assignment, {
|
assignment03 = em.create(Assignment, {
|
||||||
id: 21002,
|
id: 21002,
|
||||||
within: classes[0],
|
within: getClass01(),
|
||||||
title: 'delete',
|
title: 'delete',
|
||||||
description: 'will be deleted',
|
description: 'will be deleted',
|
||||||
learningPathHruid: 'id02',
|
learningPathHruid: testLearningPath02.hruid,
|
||||||
learningPathLanguage: Language.English,
|
learningPathLanguage: testLearningPath02.language as Language,
|
||||||
deadline: pastDate,
|
deadline: pastDate,
|
||||||
groups: [],
|
groups: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
assignment04 = em.create(Assignment, {
|
assignment04 = em.create(Assignment, {
|
||||||
id: 21003,
|
id: 21003,
|
||||||
within: classes[0],
|
within: getClass01(),
|
||||||
title: 'another assignment',
|
title: 'another assignment',
|
||||||
description: 'with a description',
|
description: 'with a description',
|
||||||
learningPathHruid: 'id01',
|
learningPathHruid: testLearningPath01.hruid,
|
||||||
learningPathLanguage: Language.English,
|
learningPathLanguage: testLearningPath01.language as Language,
|
||||||
deadline: pastDate,
|
deadline: pastDate,
|
||||||
groups: [],
|
groups: [],
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
import { EntityManager } from '@mikro-orm/core';
|
import { EntityManager } from '@mikro-orm/core';
|
||||||
import { Group } from '../../../src/entities/assignments/group.entity';
|
import { Group } from '../../../src/entities/assignments/group.entity';
|
||||||
import { Assignment } from '../../../src/entities/assignments/assignment.entity';
|
import { getAssignment01, getAssignment02, getAssignment04, getConditionalPathAssignment } from './assignments.testdata';
|
||||||
import { Student } from '../../../src/entities/users/student.entity';
|
import { getDireStraits, getNoordkaap, getPinkFloyd, getSmashingPumpkins, getTestleerling1, getTheDoors, getTool } from '../users/students.testdata';
|
||||||
import { getConditionalPathAssignment } from './assignments.testdata';
|
|
||||||
import { getTestleerling1 } from '../users/students.testdata';
|
|
||||||
|
|
||||||
export function makeTestGroups(em: EntityManager, students: Student[], assignments: Assignment[]): Group[] {
|
export function makeTestGroups(em: EntityManager): Group[] {
|
||||||
/*
|
/*
|
||||||
* Group #1 for Assignment #1 in class 'id01'
|
* Group #1 for Assignment #1 in class 'id01'
|
||||||
* => Assigned to do learning path 'id02'
|
* => Assigned to do learning path 'id02'
|
||||||
*/
|
*/
|
||||||
|
// Gets deleted
|
||||||
group01 = em.create(Group, {
|
group01 = em.create(Group, {
|
||||||
assignment: assignments[0],
|
assignment: getAssignment01(),
|
||||||
groupNumber: 21001,
|
groupNumber: 21001,
|
||||||
members: students.slice(0, 2),
|
members: [getNoordkaap(), getDireStraits()],
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -21,9 +20,9 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen
|
||||||
* => Assigned to do learning path 'id02'
|
* => Assigned to do learning path 'id02'
|
||||||
*/
|
*/
|
||||||
group02 = em.create(Group, {
|
group02 = em.create(Group, {
|
||||||
assignment: assignments[0],
|
assignment: getAssignment01(),
|
||||||
groupNumber: 21002,
|
groupNumber: 21002,
|
||||||
members: students.slice(2, 4),
|
members: [getTool(), getSmashingPumpkins()],
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -31,9 +30,9 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen
|
||||||
* => Assigned to do learning path 'id02'
|
* => Assigned to do learning path 'id02'
|
||||||
*/
|
*/
|
||||||
group03 = em.create(Group, {
|
group03 = em.create(Group, {
|
||||||
assignment: assignments[0],
|
assignment: getAssignment01(),
|
||||||
groupNumber: 21003,
|
groupNumber: 21003,
|
||||||
members: students.slice(4, 6),
|
members: [getPinkFloyd(), getTheDoors()],
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -41,9 +40,9 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen
|
||||||
* => Assigned to do learning path 'id01'
|
* => Assigned to do learning path 'id01'
|
||||||
*/
|
*/
|
||||||
group04 = em.create(Group, {
|
group04 = em.create(Group, {
|
||||||
assignment: assignments[1],
|
assignment: getAssignment02(),
|
||||||
groupNumber: 21004,
|
groupNumber: 21004,
|
||||||
members: students.slice(3, 4),
|
members: getSmashingPumpkins(),
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -51,9 +50,9 @@ export function makeTestGroups(em: EntityManager, students: Student[], assignmen
|
||||||
* => Assigned to do learning path 'id01'
|
* => Assigned to do learning path 'id01'
|
||||||
*/
|
*/
|
||||||
group05 = em.create(Group, {
|
group05 = em.create(Group, {
|
||||||
assignment: assignments[3],
|
assignment: getAssignment04(),
|
||||||
groupNumber: 21001,
|
groupNumber: 21001,
|
||||||
members: students.slice(0, 2),
|
members: [getNoordkaap(), getDireStraits()],
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,97 +1,139 @@
|
||||||
import { EntityManager } from '@mikro-orm/core';
|
import { EntityManager } from '@mikro-orm/core';
|
||||||
import { Submission } from '../../../src/entities/assignments/submission.entity';
|
import { Submission } from '../../../src/entities/assignments/submission.entity';
|
||||||
import { Language } from '@dwengo-1/common/util/language';
|
import { testLearningObject01, testLearningObject02, testLearningObject03 } from '../content/learning-objects.testdata';
|
||||||
import { Student } from '../../../src/entities/users/student.entity';
|
import { getDireStraits, getNoordkaap, getSmashingPumpkins } from '../users/students.testdata';
|
||||||
import { Group } from '../../../src/entities/assignments/group.entity';
|
import { getTestGroup01, getTestGroup02, getTestGroup04, getTestGroup05 } from './groups.testdata';
|
||||||
|
|
||||||
export function makeTestSubmissions(em: EntityManager, students: Student[], groups: Group[]): Submission[] {
|
export function makeTestSubmissions(em: EntityManager): Submission[] {
|
||||||
const submission01 = em.create(Submission, {
|
submission01 = em.create(Submission, {
|
||||||
learningObjectHruid: 'id03',
|
learningObjectHruid: testLearningObject03.hruid,
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject03.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject03.version,
|
||||||
submissionNumber: 1,
|
submissionNumber: 1,
|
||||||
submitter: students[0],
|
submitter: getNoordkaap(),
|
||||||
submissionTime: new Date(2025, 2, 20),
|
submissionTime: new Date(2025, 2, 20),
|
||||||
onBehalfOf: groups[0], // Group #1 for Assignment #1 in class 'id01'
|
onBehalfOf: getTestGroup01(), // Group #1 for Assignment #1 in class 'id01'
|
||||||
content: 'sub1',
|
content: 'sub1',
|
||||||
});
|
});
|
||||||
|
|
||||||
const submission02 = em.create(Submission, {
|
submission02 = em.create(Submission, {
|
||||||
learningObjectHruid: 'id03',
|
learningObjectHruid: testLearningObject03.hruid,
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject03.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject03.version,
|
||||||
submissionNumber: 2,
|
submissionNumber: 2,
|
||||||
submitter: students[0],
|
submitter: getNoordkaap(),
|
||||||
submissionTime: new Date(2025, 2, 25),
|
submissionTime: new Date(2025, 2, 25),
|
||||||
onBehalfOf: groups[0], // Group #1 for Assignment #1 in class 'id01'
|
onBehalfOf: getTestGroup01(), // Group #1 for Assignment #1 in class 'id01'
|
||||||
content: '',
|
content: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const submission03 = em.create(Submission, {
|
submission03 = em.create(Submission, {
|
||||||
learningObjectHruid: 'id02',
|
learningObjectHruid: testLearningObject02.hruid,
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject02.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject02.version,
|
||||||
submissionNumber: 1,
|
submissionNumber: 1,
|
||||||
submitter: students[0],
|
submitter: getNoordkaap(),
|
||||||
submissionTime: new Date(2025, 2, 20),
|
submissionTime: new Date(2025, 2, 20),
|
||||||
onBehalfOf: groups[0], // Group #1 for Assignment #1 in class 'id01'
|
onBehalfOf: getTestGroup01(), // Group #1 for Assignment #1 in class 'id01'
|
||||||
content: '',
|
content: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const submission04 = em.create(Submission, {
|
submission04 = em.create(Submission, {
|
||||||
learningObjectHruid: 'id02',
|
learningObjectHruid: testLearningObject02.hruid,
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject02.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject02.version,
|
||||||
submissionNumber: 2,
|
submissionNumber: 2,
|
||||||
submitter: students[0],
|
submitter: getNoordkaap(),
|
||||||
submissionTime: new Date(2025, 2, 25),
|
submissionTime: new Date(2025, 2, 25),
|
||||||
onBehalfOf: groups[0], // Group #1 for Assignment #1 in class 'id01'
|
onBehalfOf: getTestGroup01(), // Group #1 for Assignment #1 in class 'id01'
|
||||||
content: '',
|
content: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const submission05 = em.create(Submission, {
|
submission05 = em.create(Submission, {
|
||||||
learningObjectHruid: 'id01',
|
learningObjectHruid: testLearningObject01.hruid,
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject01.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject01.version,
|
||||||
submissionNumber: 1,
|
submissionNumber: 1,
|
||||||
submitter: students[1],
|
submitter: getDireStraits(),
|
||||||
submissionTime: new Date(2025, 2, 20),
|
submissionTime: new Date(2025, 2, 20),
|
||||||
onBehalfOf: groups[1], // Group #2 for Assignment #1 in class 'id01'
|
onBehalfOf: getTestGroup02(), // Group #2 for Assignment #1 in class 'id01'
|
||||||
content: '',
|
content: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const submission06 = em.create(Submission, {
|
submission06 = em.create(Submission, {
|
||||||
learningObjectHruid: 'id01',
|
learningObjectHruid: testLearningObject01.hruid,
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject01.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject01.version,
|
||||||
submissionNumber: 2,
|
submissionNumber: 2,
|
||||||
submitter: students[1],
|
submitter: getDireStraits(),
|
||||||
submissionTime: new Date(2025, 2, 25),
|
submissionTime: new Date(2025, 2, 25),
|
||||||
onBehalfOf: groups[4], // Group #5 for Assignment #4 in class 'id01'
|
onBehalfOf: getTestGroup05(), // Group #5 for Assignment #4 in class 'id01'
|
||||||
content: '',
|
content: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const submission07 = em.create(Submission, {
|
// Gets deleted
|
||||||
learningObjectHruid: 'id01',
|
submission07 = em.create(Submission, {
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectHruid: testLearningObject01.hruid,
|
||||||
learningObjectVersion: 1,
|
learningObjectLanguage: testLearningObject01.language,
|
||||||
|
learningObjectVersion: testLearningObject01.version,
|
||||||
submissionNumber: 3,
|
submissionNumber: 3,
|
||||||
submitter: students[3],
|
submitter: getSmashingPumpkins(),
|
||||||
submissionTime: new Date(2025, 3, 25),
|
submissionTime: new Date(2025, 3, 25),
|
||||||
onBehalfOf: groups[3], // Group #4 for Assignment #2 in class 'id02'
|
onBehalfOf: getTestGroup04(), // Group #4 for Assignment #2 in class 'id02'
|
||||||
content: '',
|
content: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const submission08 = em.create(Submission, {
|
submission08 = em.create(Submission, {
|
||||||
learningObjectHruid: 'id02',
|
learningObjectHruid: testLearningObject02.hruid,
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject02.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject02.version,
|
||||||
submissionNumber: 3,
|
submissionNumber: 3,
|
||||||
submitter: students[1],
|
submitter: getDireStraits(),
|
||||||
submissionTime: new Date(2025, 4, 7),
|
submissionTime: new Date(2025, 4, 7),
|
||||||
onBehalfOf: groups[1], // Group #2 for Assignment #1 in class 'id01'
|
onBehalfOf: getTestGroup02(), // Group #2 for Assignment #1 in class 'id01'
|
||||||
content: '',
|
content: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
return [submission01, submission02, submission03, submission04, submission05, submission06, submission07, submission08];
|
return [submission01, submission02, submission03, submission04, submission05, submission06, submission07, submission08];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let submission01: Submission;
|
||||||
|
let submission02: Submission;
|
||||||
|
let submission03: Submission;
|
||||||
|
let submission04: Submission;
|
||||||
|
let submission05: Submission;
|
||||||
|
let submission06: Submission;
|
||||||
|
let submission07: Submission;
|
||||||
|
let submission08: Submission;
|
||||||
|
|
||||||
|
export function getSubmission01(): Submission {
|
||||||
|
return submission01;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubmission02(): Submission {
|
||||||
|
return submission02;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubmission03(): Submission {
|
||||||
|
return submission03;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubmission04(): Submission {
|
||||||
|
return submission04;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubmission05(): Submission {
|
||||||
|
return submission05;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubmission06(): Submission {
|
||||||
|
return submission06;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubmission07(): Submission {
|
||||||
|
return submission07;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubmission08(): Submission {
|
||||||
|
return submission08;
|
||||||
|
}
|
||||||
|
|
|
@ -1,33 +1,54 @@
|
||||||
import { EntityManager } from '@mikro-orm/core';
|
import { EntityManager } from '@mikro-orm/core';
|
||||||
import { ClassJoinRequest } from '../../../src/entities/classes/class-join-request.entity';
|
import { ClassJoinRequest } from '../../../src/entities/classes/class-join-request.entity';
|
||||||
import { Student } from '../../../src/entities/users/student.entity';
|
|
||||||
import { Class } from '../../../src/entities/classes/class.entity';
|
|
||||||
import { ClassStatus } from '@dwengo-1/common/util/class-join-request';
|
import { ClassStatus } from '@dwengo-1/common/util/class-join-request';
|
||||||
|
import { getPinkFloyd, getSmashingPumpkins, getTool } from '../users/students.testdata';
|
||||||
|
import { getClass02, getClass03 } from './classes.testdata';
|
||||||
|
|
||||||
export function makeTestClassJoinRequests(em: EntityManager, students: Student[], classes: Class[]): ClassJoinRequest[] {
|
export function makeTestClassJoinRequests(em: EntityManager): ClassJoinRequest[] {
|
||||||
const classJoinRequest01 = em.create(ClassJoinRequest, {
|
classJoinRequest01 = em.create(ClassJoinRequest, {
|
||||||
requester: students[4],
|
requester: getPinkFloyd(),
|
||||||
class: classes[1],
|
class: getClass02(),
|
||||||
status: ClassStatus.Open,
|
status: ClassStatus.Open,
|
||||||
});
|
});
|
||||||
|
|
||||||
const classJoinRequest02 = em.create(ClassJoinRequest, {
|
classJoinRequest02 = em.create(ClassJoinRequest, {
|
||||||
requester: students[2],
|
requester: getTool(),
|
||||||
class: classes[1],
|
class: getClass02(),
|
||||||
status: ClassStatus.Open,
|
status: ClassStatus.Open,
|
||||||
});
|
});
|
||||||
|
|
||||||
const classJoinRequest03 = em.create(ClassJoinRequest, {
|
classJoinRequest03 = em.create(ClassJoinRequest, {
|
||||||
requester: students[4],
|
requester: getPinkFloyd(),
|
||||||
class: classes[2],
|
class: getClass03(),
|
||||||
status: ClassStatus.Open,
|
status: ClassStatus.Open,
|
||||||
});
|
});
|
||||||
|
|
||||||
const classJoinRequest04 = em.create(ClassJoinRequest, {
|
classJoinRequest04 = em.create(ClassJoinRequest, {
|
||||||
requester: students[3],
|
requester: getSmashingPumpkins(),
|
||||||
class: classes[2],
|
class: getClass03(),
|
||||||
status: ClassStatus.Open,
|
status: ClassStatus.Open,
|
||||||
});
|
});
|
||||||
|
|
||||||
return [classJoinRequest01, classJoinRequest02, classJoinRequest03, classJoinRequest04];
|
return [classJoinRequest01, classJoinRequest02, classJoinRequest03, classJoinRequest04];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let classJoinRequest01: ClassJoinRequest;
|
||||||
|
let classJoinRequest02: ClassJoinRequest;
|
||||||
|
let classJoinRequest03: ClassJoinRequest;
|
||||||
|
let classJoinRequest04: ClassJoinRequest;
|
||||||
|
|
||||||
|
export function getClassJoinRequest01(): ClassJoinRequest {
|
||||||
|
return classJoinRequest01;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getClassJoinRequest02(): ClassJoinRequest {
|
||||||
|
return classJoinRequest02;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getClassJoinRequest03(): ClassJoinRequest {
|
||||||
|
return classJoinRequest03;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getClassJoinRequest04(): ClassJoinRequest {
|
||||||
|
return classJoinRequest04;
|
||||||
|
}
|
||||||
|
|
|
@ -2,12 +2,12 @@ import { EntityManager } from '@mikro-orm/core';
|
||||||
import { Class } from '../../../src/entities/classes/class.entity';
|
import { Class } from '../../../src/entities/classes/class.entity';
|
||||||
import { Student } from '../../../src/entities/users/student.entity';
|
import { Student } from '../../../src/entities/users/student.entity';
|
||||||
import { Teacher } from '../../../src/entities/users/teacher.entity';
|
import { Teacher } from '../../../src/entities/users/teacher.entity';
|
||||||
import { getTestleerkracht1 } from '../users/teachers.testdata';
|
import { getLimpBizkit, getStaind, getTestleerkracht1 } from '../users/teachers.testdata';
|
||||||
import { getTestleerling1 } from '../users/students.testdata';
|
import { getDireStraits, getNoordkaap, getSmashingPumpkins, getTestleerling1, getTool } from '../users/students.testdata';
|
||||||
|
|
||||||
export function makeTestClasses(em: EntityManager, students: Student[], teachers: Teacher[]): Class[] {
|
export function makeTestClasses(em: EntityManager): Class[] {
|
||||||
const studentsClass01 = students.slice(0, 8);
|
const studentsClass01 = [getTestleerling1()];
|
||||||
const teacherClass01: Teacher[] = teachers.slice(4, 5);
|
const teacherClass01: Teacher[] = [getTestleerkracht1()];
|
||||||
|
|
||||||
class01 = em.create(Class, {
|
class01 = em.create(Class, {
|
||||||
classId: 'X2J9QT', // 8764b861-90a6-42e5-9732-c0d9eb2f55f9
|
classId: 'X2J9QT', // 8764b861-90a6-42e5-9732-c0d9eb2f55f9
|
||||||
|
@ -16,8 +16,8 @@ export function makeTestClasses(em: EntityManager, students: Student[], teachers
|
||||||
students: studentsClass01,
|
students: studentsClass01,
|
||||||
});
|
});
|
||||||
|
|
||||||
const studentsClass02: Student[] = students.slice(0, 2).concat(students.slice(3, 4));
|
const studentsClass02: Student[] = [getNoordkaap(), getDireStraits(), getSmashingPumpkins()];
|
||||||
const teacherClass02: Teacher[] = teachers.slice(1, 2);
|
const teacherClass02: Teacher[] = [getLimpBizkit()];
|
||||||
|
|
||||||
class02 = em.create(Class, {
|
class02 = em.create(Class, {
|
||||||
classId: '7KLPMA', // 34d484a1-295f-4e9f-bfdc-3e7a23d86a89
|
classId: '7KLPMA', // 34d484a1-295f-4e9f-bfdc-3e7a23d86a89
|
||||||
|
@ -26,8 +26,8 @@ export function makeTestClasses(em: EntityManager, students: Student[], teachers
|
||||||
students: studentsClass02,
|
students: studentsClass02,
|
||||||
});
|
});
|
||||||
|
|
||||||
const studentsClass03: Student[] = students.slice(1, 4);
|
const studentsClass03: Student[] = [getDireStraits(), getTool(), getSmashingPumpkins()];
|
||||||
const teacherClass03: Teacher[] = teachers.slice(2, 3);
|
const teacherClass03: Teacher[] = [getStaind()];
|
||||||
|
|
||||||
class03 = em.create(Class, {
|
class03 = em.create(Class, {
|
||||||
classId: 'R0D3UZ', // 80dcc3e0-1811-4091-9361-42c0eee91cfa
|
classId: 'R0D3UZ', // 80dcc3e0-1811-4091-9361-42c0eee91cfa
|
||||||
|
@ -36,9 +36,10 @@ export function makeTestClasses(em: EntityManager, students: Student[], teachers
|
||||||
students: studentsClass03,
|
students: studentsClass03,
|
||||||
});
|
});
|
||||||
|
|
||||||
const studentsClass04: Student[] = students.slice(0, 2);
|
const studentsClass04: Student[] = [getNoordkaap(), getDireStraits()];
|
||||||
const teacherClass04: Teacher[] = teachers.slice(2, 3);
|
const teacherClass04: Teacher[] = [getStaind()];
|
||||||
|
|
||||||
|
// Gets deleted in test
|
||||||
class04 = em.create(Class, {
|
class04 = em.create(Class, {
|
||||||
classId: 'Q8N5YC', // 33d03536-83b8-4880-9982-9bbf2f908ddf
|
classId: 'Q8N5YC', // 33d03536-83b8-4880-9982-9bbf2f908ddf
|
||||||
displayName: 'class04',
|
displayName: 'class04',
|
||||||
|
|
|
@ -1,37 +1,59 @@
|
||||||
import { EntityManager } from '@mikro-orm/core';
|
import { EntityManager } from '@mikro-orm/core';
|
||||||
import { TeacherInvitation } from '../../../src/entities/classes/teacher-invitation.entity';
|
import { TeacherInvitation } from '../../../src/entities/classes/teacher-invitation.entity';
|
||||||
import { Teacher } from '../../../src/entities/users/teacher.entity';
|
|
||||||
import { Class } from '../../../src/entities/classes/class.entity';
|
|
||||||
import { ClassStatus } from '@dwengo-1/common/util/class-join-request';
|
import { ClassStatus } from '@dwengo-1/common/util/class-join-request';
|
||||||
|
import { getFooFighters, getLimpBizkit, getStaind } from '../users/teachers.testdata';
|
||||||
|
import { getClass01, getClass02, getClass03 } from './classes.testdata';
|
||||||
|
|
||||||
export function makeTestTeacherInvitations(em: EntityManager, teachers: Teacher[], classes: Class[]): TeacherInvitation[] {
|
export function makeTestTeacherInvitations(em: EntityManager): TeacherInvitation[] {
|
||||||
const teacherInvitation01 = em.create(TeacherInvitation, {
|
teacherInvitation01 = em.create(TeacherInvitation, {
|
||||||
sender: teachers[1],
|
sender: getLimpBizkit(),
|
||||||
receiver: teachers[0],
|
receiver: getFooFighters(),
|
||||||
class: classes[1],
|
class: getClass02(),
|
||||||
status: ClassStatus.Open,
|
status: ClassStatus.Open,
|
||||||
});
|
});
|
||||||
|
|
||||||
const teacherInvitation02 = em.create(TeacherInvitation, {
|
teacherInvitation02 = em.create(TeacherInvitation, {
|
||||||
sender: teachers[1],
|
sender: getLimpBizkit(),
|
||||||
receiver: teachers[2],
|
receiver: getStaind(),
|
||||||
class: classes[1],
|
class: getClass02(),
|
||||||
status: ClassStatus.Open,
|
status: ClassStatus.Open,
|
||||||
});
|
});
|
||||||
|
|
||||||
const teacherInvitation03 = em.create(TeacherInvitation, {
|
teacherInvitation03 = em.create(TeacherInvitation, {
|
||||||
sender: teachers[2],
|
sender: getStaind(),
|
||||||
receiver: teachers[0],
|
receiver: getFooFighters(),
|
||||||
class: classes[2],
|
class: getClass03(),
|
||||||
status: ClassStatus.Open,
|
status: ClassStatus.Open,
|
||||||
});
|
});
|
||||||
|
|
||||||
const teacherInvitation04 = em.create(TeacherInvitation, {
|
// Gets deleted in test
|
||||||
sender: teachers[0],
|
teacherInvitation04 = em.create(TeacherInvitation, {
|
||||||
receiver: teachers[1],
|
sender: getFooFighters(),
|
||||||
class: classes[0],
|
receiver: getLimpBizkit(),
|
||||||
|
class: getClass01(),
|
||||||
status: ClassStatus.Open,
|
status: ClassStatus.Open,
|
||||||
});
|
});
|
||||||
|
|
||||||
return [teacherInvitation01, teacherInvitation02, teacherInvitation03, teacherInvitation04];
|
return [teacherInvitation01, teacherInvitation02, teacherInvitation03, teacherInvitation04];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let teacherInvitation01: TeacherInvitation;
|
||||||
|
let teacherInvitation02: TeacherInvitation;
|
||||||
|
let teacherInvitation03: TeacherInvitation;
|
||||||
|
let teacherInvitation04: TeacherInvitation;
|
||||||
|
|
||||||
|
export function getTeacherInvitation01(): TeacherInvitation {
|
||||||
|
return teacherInvitation01;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTeacherInvitation02(): TeacherInvitation {
|
||||||
|
return teacherInvitation02;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTeacherInvitation03(): TeacherInvitation {
|
||||||
|
return teacherInvitation03;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTeacherInvitation04(): TeacherInvitation {
|
||||||
|
return teacherInvitation04;
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import { EntityManager } from '@mikro-orm/core';
|
import { EntityManager } from '@mikro-orm/core';
|
||||||
import { Attachment } from '../../../src/entities/content/attachment.entity';
|
import { Attachment } from '../../../src/entities/content/attachment.entity';
|
||||||
|
import { testLearningObject01 } from './learning-objects.testdata';
|
||||||
import { LearningObject } from '../../../src/entities/content/learning-object.entity';
|
import { LearningObject } from '../../../src/entities/content/learning-object.entity';
|
||||||
|
|
||||||
export function makeTestAttachments(em: EntityManager, learningObjects: LearningObject[]): Attachment[] {
|
export function makeTestAttachments(em: EntityManager): Attachment[] {
|
||||||
const attachment01 = em.create(Attachment, {
|
// Prevent duplicate insertion
|
||||||
learningObject: learningObjects[1],
|
const lo = em.merge(LearningObject, testLearningObject01);
|
||||||
|
|
||||||
|
attachment01 = em.create(Attachment, {
|
||||||
|
learningObject: lo,
|
||||||
name: 'attachment01',
|
name: 'attachment01',
|
||||||
mimeType: '',
|
mimeType: '',
|
||||||
content: Buffer.from(''),
|
content: Buffer.from(''),
|
||||||
|
@ -12,3 +16,9 @@ export function makeTestAttachments(em: EntityManager, learningObjects: Learning
|
||||||
|
|
||||||
return [attachment01];
|
return [attachment01];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let attachment01: Attachment;
|
||||||
|
|
||||||
|
export function getAttachment01(): Attachment {
|
||||||
|
return attachment01;
|
||||||
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ export const testLearningObject02: RequiredEntityData<LearningObject> = {
|
||||||
description: 'second album',
|
description: 'second album',
|
||||||
contentType: DwengoContentType.TEXT_MARKDOWN,
|
contentType: DwengoContentType.TEXT_MARKDOWN,
|
||||||
keywords: [],
|
keywords: [],
|
||||||
|
uuid: v4(),
|
||||||
teacherExclusive: false,
|
teacherExclusive: false,
|
||||||
skosConcepts: [],
|
skosConcepts: [],
|
||||||
educationalGoals: [],
|
educationalGoals: [],
|
||||||
|
@ -99,6 +100,7 @@ export const testLearningObject03: RequiredEntityData<LearningObject> = {
|
||||||
description: 'third album',
|
description: 'third album',
|
||||||
contentType: DwengoContentType.TEXT_MARKDOWN,
|
contentType: DwengoContentType.TEXT_MARKDOWN,
|
||||||
keywords: [],
|
keywords: [],
|
||||||
|
uuid: v4(),
|
||||||
teacherExclusive: false,
|
teacherExclusive: false,
|
||||||
skosConcepts: [],
|
skosConcepts: [],
|
||||||
educationalGoals: [],
|
educationalGoals: [],
|
||||||
|
@ -126,6 +128,7 @@ export const testLearningObject04: RequiredEntityData<LearningObject> = {
|
||||||
description: 'fifth album',
|
description: 'fifth album',
|
||||||
contentType: DwengoContentType.TEXT_MARKDOWN,
|
contentType: DwengoContentType.TEXT_MARKDOWN,
|
||||||
keywords: [],
|
keywords: [],
|
||||||
|
uuid: v4(),
|
||||||
teacherExclusive: false,
|
teacherExclusive: false,
|
||||||
skosConcepts: [],
|
skosConcepts: [],
|
||||||
educationalGoals: [],
|
educationalGoals: [],
|
||||||
|
@ -153,6 +156,7 @@ export const testLearningObject05: RequiredEntityData<LearningObject> = {
|
||||||
description: 'sixth album',
|
description: 'sixth album',
|
||||||
contentType: DwengoContentType.TEXT_MARKDOWN,
|
contentType: DwengoContentType.TEXT_MARKDOWN,
|
||||||
keywords: [],
|
keywords: [],
|
||||||
|
uuid: v4(),
|
||||||
teacherExclusive: false,
|
teacherExclusive: false,
|
||||||
skosConcepts: [],
|
skosConcepts: [],
|
||||||
educationalGoals: [],
|
educationalGoals: [],
|
||||||
|
@ -173,6 +177,7 @@ export const testLearningObjectMultipleChoice: RequiredEntityData<LearningObject
|
||||||
title: 'Self-evaluation',
|
title: 'Self-evaluation',
|
||||||
description: "Time to evaluate how well you understand what you've learned so far.",
|
description: "Time to evaluate how well you understand what you've learned so far.",
|
||||||
keywords: ['test'],
|
keywords: ['test'],
|
||||||
|
uuid: v4(),
|
||||||
teacherExclusive: false,
|
teacherExclusive: false,
|
||||||
skosConcepts: [],
|
skosConcepts: [],
|
||||||
educationalGoals: [],
|
educationalGoals: [],
|
||||||
|
@ -199,6 +204,7 @@ export const testLearningObjectEssayQuestion: RequiredEntityData<LearningObject>
|
||||||
title: 'Reflection',
|
title: 'Reflection',
|
||||||
description: 'Reflect on your learning progress.',
|
description: 'Reflect on your learning progress.',
|
||||||
keywords: ['test'],
|
keywords: ['test'],
|
||||||
|
uuid: v4(),
|
||||||
teacherExclusive: false,
|
teacherExclusive: false,
|
||||||
skosConcepts: [],
|
skosConcepts: [],
|
||||||
educationalGoals: [],
|
educationalGoals: [],
|
||||||
|
|
|
@ -1,36 +1,35 @@
|
||||||
import { EntityManager } from '@mikro-orm/core';
|
import { EntityManager } from '@mikro-orm/core';
|
||||||
import { Answer } from '../../../src/entities/questions/answer.entity';
|
import { Answer } from '../../../src/entities/questions/answer.entity';
|
||||||
import { Teacher } from '../../../src/entities/users/teacher.entity';
|
import { getFooFighters, getLimpBizkit, getTestleerkracht1 } from '../users/teachers.testdata';
|
||||||
import { Question } from '../../../src/entities/questions/question.entity';
|
import { getQuestion02, getQuestion04, getQuestion07 } from './questions.testdata';
|
||||||
import { getTestleerkracht1 } from '../users/teachers.testdata';
|
|
||||||
import { getQuestion07 } from './questions.testdata';
|
|
||||||
|
|
||||||
export function makeTestAnswers(em: EntityManager, teachers: Teacher[], questions: Question[]): Answer[] {
|
export function makeTestAnswers(em: EntityManager): Answer[] {
|
||||||
const answer01 = em.create(Answer, {
|
answer01 = em.create(Answer, {
|
||||||
author: teachers[0],
|
author: getFooFighters(),
|
||||||
toQuestion: questions[1],
|
toQuestion: getQuestion02(),
|
||||||
sequenceNumber: 1,
|
sequenceNumber: 1,
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
content: 'answer',
|
content: 'answer',
|
||||||
});
|
});
|
||||||
|
|
||||||
const answer02 = em.create(Answer, {
|
answer02 = em.create(Answer, {
|
||||||
author: teachers[0],
|
author: getFooFighters(),
|
||||||
toQuestion: questions[1],
|
toQuestion: getQuestion02(),
|
||||||
sequenceNumber: 2,
|
sequenceNumber: 2,
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
content: 'answer2',
|
content: 'answer2',
|
||||||
});
|
});
|
||||||
|
|
||||||
const answer03 = em.create(Answer, {
|
// Gets deleted
|
||||||
author: teachers[1],
|
answer03 = em.create(Answer, {
|
||||||
toQuestion: questions[3],
|
author: getLimpBizkit(),
|
||||||
|
toQuestion: getQuestion04(),
|
||||||
sequenceNumber: 1,
|
sequenceNumber: 1,
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
content: 'answer3',
|
content: 'answer3',
|
||||||
});
|
});
|
||||||
|
|
||||||
const answer04 = em.create(Answer, {
|
answer04 = em.create(Answer, {
|
||||||
author: getTestleerkracht1(),
|
author: getTestleerkracht1(),
|
||||||
toQuestion: getQuestion07(),
|
toQuestion: getQuestion07(),
|
||||||
sequenceNumber: 1,
|
sequenceNumber: 1,
|
||||||
|
@ -38,7 +37,7 @@ export function makeTestAnswers(em: EntityManager, teachers: Teacher[], question
|
||||||
content: 'this is a test answer',
|
content: 'this is a test answer',
|
||||||
});
|
});
|
||||||
|
|
||||||
const answer05 = em.create(Answer, {
|
answer05 = em.create(Answer, {
|
||||||
author: getTestleerkracht1(),
|
author: getTestleerkracht1(),
|
||||||
toQuestion: getQuestion07(),
|
toQuestion: getQuestion07(),
|
||||||
sequenceNumber: 2,
|
sequenceNumber: 2,
|
||||||
|
@ -48,3 +47,29 @@ export function makeTestAnswers(em: EntityManager, teachers: Teacher[], question
|
||||||
|
|
||||||
return [answer01, answer02, answer03, answer04, answer05];
|
return [answer01, answer02, answer03, answer04, answer05];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let answer01: Answer;
|
||||||
|
let answer02: Answer;
|
||||||
|
let answer03: Answer;
|
||||||
|
let answer04: Answer;
|
||||||
|
let answer05: Answer;
|
||||||
|
|
||||||
|
export function getAnswer01(): Answer {
|
||||||
|
return answer01;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAnswer02(): Answer {
|
||||||
|
return answer02;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAnswer03(): Answer {
|
||||||
|
return answer03;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAnswer04(): Answer {
|
||||||
|
return answer04;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAnswer05(): Answer {
|
||||||
|
return answer05;
|
||||||
|
}
|
||||||
|
|
|
@ -1,82 +1,85 @@
|
||||||
import { EntityManager } from '@mikro-orm/core';
|
import { EntityManager } from '@mikro-orm/core';
|
||||||
import { Question } from '../../../src/entities/questions/question.entity';
|
import { Question } from '../../../src/entities/questions/question.entity';
|
||||||
import { Language } from '@dwengo-1/common/util/language';
|
import { getDireStraits, getNoordkaap, getTestleerling1, getTool } from '../users/students.testdata';
|
||||||
import { Student } from '../../../src/entities/users/student.entity';
|
import {
|
||||||
import { Group } from '../../../src/entities/assignments/group.entity';
|
testLearningObject01,
|
||||||
import { getTestleerling1 } from '../users/students.testdata';
|
testLearningObject04,
|
||||||
import { testLearningObjectMultipleChoice } from '../content/learning-objects.testdata';
|
testLearningObject05,
|
||||||
import { getGroup1ConditionalLearningPath } from '../assignments/groups.testdata';
|
testLearningObjectMultipleChoice,
|
||||||
|
} from '../content/learning-objects.testdata';
|
||||||
|
import { getGroup1ConditionalLearningPath, getTestGroup01, getTestGroup02 } from '../assignments/groups.testdata';
|
||||||
|
|
||||||
export function makeTestQuestions(em: EntityManager, students: Student[], groups: Group[]): Question[] {
|
export function makeTestQuestions(em: EntityManager): Question[] {
|
||||||
const question01 = em.create(Question, {
|
question01 = em.create(Question, {
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject05.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject05.version,
|
||||||
learningObjectHruid: 'id05',
|
learningObjectHruid: testLearningObject05.hruid,
|
||||||
inGroup: groups[0], // Group #1 for Assignment #1 in class 'id01'
|
inGroup: getTestGroup01(), // Group #1 for Assignment #1 in class 'id01'
|
||||||
sequenceNumber: 1,
|
sequenceNumber: 1,
|
||||||
author: students[0],
|
author: getNoordkaap(),
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
content: 'question',
|
content: 'question',
|
||||||
});
|
});
|
||||||
|
|
||||||
const question02 = em.create(Question, {
|
question02 = em.create(Question, {
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject05.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject05.version,
|
||||||
learningObjectHruid: 'id05',
|
learningObjectHruid: testLearningObject05.hruid,
|
||||||
inGroup: groups[0], // Group #1 for Assignment #1 in class 'id01'
|
inGroup: getTestGroup01(), // Group #1 for Assignment #1 in class 'id01'
|
||||||
sequenceNumber: 2,
|
sequenceNumber: 2,
|
||||||
author: students[2],
|
author: getTool(),
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
content: 'question',
|
content: 'question',
|
||||||
});
|
});
|
||||||
|
|
||||||
const question03 = em.create(Question, {
|
//Gets deleted
|
||||||
learningObjectLanguage: Language.English,
|
question03 = em.create(Question, {
|
||||||
learningObjectVersion: 1,
|
learningObjectLanguage: testLearningObject04.language,
|
||||||
learningObjectHruid: 'id04',
|
learningObjectVersion: testLearningObject04.version,
|
||||||
|
learningObjectHruid: testLearningObject04.hruid,
|
||||||
sequenceNumber: 1,
|
sequenceNumber: 1,
|
||||||
author: students[0],
|
author: getNoordkaap(),
|
||||||
inGroup: groups[0], // Group #1 for Assignment #1 in class 'id01'
|
inGroup: getTestGroup01(), // Group #1 for Assignment #1 in class 'id01'
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
content: 'question',
|
content: 'question',
|
||||||
});
|
});
|
||||||
|
|
||||||
const question04 = em.create(Question, {
|
question04 = em.create(Question, {
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject01.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject01.version,
|
||||||
learningObjectHruid: 'id01',
|
learningObjectHruid: testLearningObject01.hruid,
|
||||||
sequenceNumber: 1,
|
sequenceNumber: 1,
|
||||||
author: students[1],
|
author: getDireStraits(),
|
||||||
inGroup: groups[1], // Group #2 for Assignment #1 in class 'id01'
|
inGroup: getTestGroup02(), // Group #2 for Assignment #1 in class 'id01'
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
content: 'question',
|
content: 'question',
|
||||||
});
|
});
|
||||||
|
|
||||||
const question05 = em.create(Question, {
|
question05 = em.create(Question, {
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject05.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject05.version,
|
||||||
learningObjectHruid: 'id05',
|
learningObjectHruid: testLearningObject05.hruid,
|
||||||
sequenceNumber: 3,
|
sequenceNumber: 3,
|
||||||
author: students[1],
|
author: getDireStraits(),
|
||||||
inGroup: groups[1], // Group #2 for Assignment #1 in class 'id01'
|
inGroup: getTestGroup02(), // Group #2 for Assignment #1 in class 'id01'
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
content: 'question',
|
content: 'question',
|
||||||
});
|
});
|
||||||
|
|
||||||
const question06 = em.create(Question, {
|
question06 = em.create(Question, {
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObject05.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObject05.version,
|
||||||
learningObjectHruid: 'id05',
|
learningObjectHruid: testLearningObject05.hruid,
|
||||||
sequenceNumber: 4,
|
sequenceNumber: 4,
|
||||||
author: students[2],
|
author: getTool(),
|
||||||
inGroup: groups[5], // Group #4 for Assignment #2 in class 'id02'
|
inGroup: getGroup1ConditionalLearningPath(), // Group #4 for Assignment #2 in class 'id02'
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
content: 'question',
|
content: 'question',
|
||||||
});
|
});
|
||||||
|
|
||||||
question07 = em.create(Question, {
|
question07 = em.create(Question, {
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObjectMultipleChoice.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObjectMultipleChoice.version,
|
||||||
learningObjectHruid: testLearningObjectMultipleChoice.hruid,
|
learningObjectHruid: testLearningObjectMultipleChoice.hruid,
|
||||||
sequenceNumber: 1,
|
sequenceNumber: 1,
|
||||||
author: getTestleerling1(),
|
author: getTestleerling1(),
|
||||||
|
@ -86,8 +89,8 @@ export function makeTestQuestions(em: EntityManager, students: Student[], groups
|
||||||
});
|
});
|
||||||
|
|
||||||
question08 = em.create(Question, {
|
question08 = em.create(Question, {
|
||||||
learningObjectLanguage: Language.English,
|
learningObjectLanguage: testLearningObjectMultipleChoice.language,
|
||||||
learningObjectVersion: 1,
|
learningObjectVersion: testLearningObjectMultipleChoice.version,
|
||||||
learningObjectHruid: testLearningObjectMultipleChoice.hruid,
|
learningObjectHruid: testLearningObjectMultipleChoice.hruid,
|
||||||
sequenceNumber: 2,
|
sequenceNumber: 2,
|
||||||
author: getTestleerling1(),
|
author: getTestleerling1(),
|
||||||
|
@ -99,8 +102,38 @@ export function makeTestQuestions(em: EntityManager, students: Student[], groups
|
||||||
return [question01, question02, question03, question04, question05, question06, question07, question08];
|
return [question01, question02, question03, question04, question05, question06, question07, question08];
|
||||||
}
|
}
|
||||||
|
|
||||||
let question08: Question;
|
let question01: Question;
|
||||||
|
let question02: Question;
|
||||||
|
let question03: Question;
|
||||||
|
let question04: Question;
|
||||||
|
let question05: Question;
|
||||||
|
let question06: Question;
|
||||||
let question07: Question;
|
let question07: Question;
|
||||||
|
let question08: Question;
|
||||||
|
|
||||||
|
export function getQuestion01(): Question {
|
||||||
|
return question01;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQuestion02(): Question {
|
||||||
|
return question02;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQuestion03(): Question {
|
||||||
|
return question03;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQuestion04(): Question {
|
||||||
|
return question04;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQuestion05(): Question {
|
||||||
|
return question05;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQuestion06(): Question {
|
||||||
|
return question06;
|
||||||
|
}
|
||||||
|
|
||||||
export function getQuestion07(): Question {
|
export function getQuestion07(): Question {
|
||||||
return question07;
|
return question07;
|
||||||
|
|
|
@ -9,7 +9,6 @@ export const TEST_STUDENTS = [
|
||||||
{ username: 'SmashingPumpkins', firstName: 'Billy', lastName: 'Corgan' },
|
{ username: 'SmashingPumpkins', firstName: 'Billy', lastName: 'Corgan' },
|
||||||
{ username: 'PinkFloyd', firstName: 'David', lastName: 'Gilmoure' },
|
{ username: 'PinkFloyd', firstName: 'David', lastName: 'Gilmoure' },
|
||||||
{ username: 'TheDoors', firstName: 'Jim', lastName: 'Morisson' },
|
{ username: 'TheDoors', firstName: 'Jim', lastName: 'Morisson' },
|
||||||
// ⚠️ Deze mag niet gebruikt worden in elke test!
|
|
||||||
{ username: 'Nirvana', firstName: 'Kurt', lastName: 'Cobain' },
|
{ username: 'Nirvana', firstName: 'Kurt', lastName: 'Cobain' },
|
||||||
// Makes sure when logged in as leerling1, there exists a corresponding user
|
// Makes sure when logged in as leerling1, there exists a corresponding user
|
||||||
{ username: 'testleerling1', firstName: 'Gerald', lastName: 'Schmittinger' },
|
{ username: 'testleerling1', firstName: 'Gerald', lastName: 'Schmittinger' },
|
||||||
|
@ -24,5 +23,33 @@ export function makeTestStudents(em: EntityManager): Student[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTestleerling1(): Student {
|
export function getTestleerling1(): Student {
|
||||||
return testStudents.find((it) => it.username === 'testleerling1');
|
return testStudents.find((it) => it.username === 'testleerling1')!;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNoordkaap(): Student {
|
||||||
|
return testStudents.find((it) => it.username === 'Noordkaap')!;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDireStraits(): Student {
|
||||||
|
return testStudents.find((it) => it.username === 'DireStraits')!;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTool(): Student {
|
||||||
|
return testStudents.find((it) => it.username === 'Tool')!;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSmashingPumpkins(): Student {
|
||||||
|
return testStudents.find((it) => it.username === 'SmashingPumpkins')!;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPinkFloyd(): Student {
|
||||||
|
return testStudents.find((it) => it.username === 'PinkFloyd')!;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTheDoors(): Student {
|
||||||
|
return testStudents.find((it) => it.username === 'TheDoors')!;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNirvana(): Student {
|
||||||
|
return testStudents.find((it) => it.username === 'Nirvana')!;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,19 +43,19 @@ let teacher03: Teacher;
|
||||||
let teacher04: Teacher;
|
let teacher04: Teacher;
|
||||||
let testleerkracht1: Teacher;
|
let testleerkracht1: Teacher;
|
||||||
|
|
||||||
export function getTeacher01(): Teacher {
|
export function getFooFighters(): Teacher {
|
||||||
return teacher01;
|
return teacher01;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTeacher02(): Teacher {
|
export function getLimpBizkit(): Teacher {
|
||||||
return teacher02;
|
return teacher02;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTeacher03(): Teacher {
|
export function getStaind(): Teacher {
|
||||||
return teacher03;
|
return teacher03;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTeacher04(): Teacher {
|
export function getZesdeMetaal(): Teacher {
|
||||||
return teacher04;
|
return teacher04;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,82 +1,23 @@
|
||||||
import { forkEntityManager, initORM } from '../src/orm.js';
|
import { initORM } from '../src/orm.js';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
import { makeTestAssignemnts } from '../tests/test_assets/assignments/assignments.testdata.js';
|
|
||||||
import { makeTestGroups } from '../tests/test_assets/assignments/groups.testdata.js';
|
|
||||||
import { makeTestSubmissions } from '../tests/test_assets/assignments/submission.testdata.js';
|
|
||||||
import { makeTestClassJoinRequests } from '../tests/test_assets/classes/class-join-requests.testdata.js';
|
|
||||||
import { makeTestClasses } from '../tests/test_assets/classes/classes.testdata.js';
|
|
||||||
import { makeTestTeacherInvitations } from '../tests/test_assets/classes/teacher-invitations.testdata.js';
|
|
||||||
import { makeTestAttachments } from '../tests/test_assets/content/attachments.testdata.js';
|
|
||||||
import { makeTestLearningObjects } from '../tests/test_assets/content/learning-objects.testdata.js';
|
|
||||||
import { makeTestLearningPaths } from '../tests/test_assets/content/learning-paths.testdata.js';
|
|
||||||
import { makeTestAnswers } from '../tests/test_assets/questions/answers.testdata.js';
|
|
||||||
import { makeTestQuestions } from '../tests/test_assets/questions/questions.testdata.js';
|
|
||||||
import { makeTestStudents } from '../tests/test_assets/users/students.testdata.js';
|
|
||||||
import { makeTestTeachers } from '../tests/test_assets/users/teachers.testdata.js';
|
|
||||||
import { getLogger, Logger } from '../src/logging/initalize.js';
|
import { getLogger, Logger } from '../src/logging/initalize.js';
|
||||||
import { Collection, MikroORM } from '@mikro-orm/core';
|
import { seedORM } from './seedORM.js';
|
||||||
import { Group } from '../src/entities/assignments/group.entity';
|
|
||||||
|
|
||||||
const logger: Logger = getLogger();
|
const logger: Logger = getLogger();
|
||||||
|
|
||||||
export async function seedORM(orm: MikroORM): Promise<void> {
|
export async function seedDatabase(
|
||||||
await orm.schema.clearDatabase();
|
envFile = '.env.development.local',
|
||||||
|
testMode = process.env.NODE_ENV !== undefined && process.env.NODE_ENV === 'test'
|
||||||
const em = forkEntityManager();
|
): Promise<void> {
|
||||||
|
|
||||||
logger.info('seeding database...');
|
|
||||||
|
|
||||||
const students = makeTestStudents(em);
|
|
||||||
const teachers = makeTestTeachers(em);
|
|
||||||
const learningObjects = makeTestLearningObjects(em);
|
|
||||||
const learningPaths = makeTestLearningPaths(em);
|
|
||||||
const classes = makeTestClasses(em, students, teachers);
|
|
||||||
const assignments = makeTestAssignemnts(em, classes);
|
|
||||||
|
|
||||||
const groups = makeTestGroups(em, students, assignments);
|
|
||||||
|
|
||||||
assignments[0].groups = new Collection<Group>(groups.slice(0, 3));
|
|
||||||
assignments[1].groups = new Collection<Group>(groups.slice(3, 4));
|
|
||||||
|
|
||||||
const teacherInvitations = makeTestTeacherInvitations(em, teachers, classes);
|
|
||||||
const classJoinRequests = makeTestClassJoinRequests(em, students, classes);
|
|
||||||
const attachments = makeTestAttachments(em, learningObjects);
|
|
||||||
|
|
||||||
learningObjects[1].attachments = attachments;
|
|
||||||
|
|
||||||
const questions = makeTestQuestions(em, students, groups);
|
|
||||||
const answers = makeTestAnswers(em, teachers, questions);
|
|
||||||
const submissions = makeTestSubmissions(em, students, groups);
|
|
||||||
|
|
||||||
// Persist all entities
|
|
||||||
await em.persistAndFlush([
|
|
||||||
...students,
|
|
||||||
...teachers,
|
|
||||||
...learningObjects,
|
|
||||||
...learningPaths,
|
|
||||||
...classes,
|
|
||||||
...assignments,
|
|
||||||
...groups,
|
|
||||||
...teacherInvitations,
|
|
||||||
...classJoinRequests,
|
|
||||||
...attachments,
|
|
||||||
...questions,
|
|
||||||
...answers,
|
|
||||||
...submissions,
|
|
||||||
]);
|
|
||||||
|
|
||||||
logger.info('Development database seeded successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function seedDatabase(envFile = '.env.development.local', testMode = false): Promise<void> {
|
|
||||||
dotenv.config({ path: envFile });
|
dotenv.config({ path: envFile });
|
||||||
|
|
||||||
|
try {
|
||||||
const orm = await initORM(testMode);
|
const orm = await initORM(testMode);
|
||||||
|
|
||||||
await seedORM(orm);
|
await seedORM(orm);
|
||||||
|
|
||||||
await orm.close();
|
await orm.close();
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`Error: ${err}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
seedDatabase().catch((err) => {
|
seedDatabase().catch((err) => logger.error(`Seeding: ${err}`));
|
||||||
logger.error(err);
|
|
||||||
});
|
|
||||||
|
|
70
backend/tool/seedORM.ts
Normal file
70
backend/tool/seedORM.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { Collection, MikroORM } from '@mikro-orm/core';
|
||||||
|
import { forkEntityManager } from '../src/orm';
|
||||||
|
import { makeTestStudents } from '../tests/test_assets/users/students.testdata';
|
||||||
|
import { makeTestTeachers } from '../tests/test_assets/users/teachers.testdata';
|
||||||
|
import { makeTestLearningObjects } from '../tests/test_assets/content/learning-objects.testdata';
|
||||||
|
import { makeTestLearningPaths } from '../tests/test_assets/content/learning-paths.testdata';
|
||||||
|
import { makeTestClasses } from '../tests/test_assets/classes/classes.testdata';
|
||||||
|
import { makeTestAssignemnts } from '../tests/test_assets/assignments/assignments.testdata';
|
||||||
|
import { getTestGroup01, getTestGroup02, getTestGroup03, getTestGroup04, makeTestGroups } from '../tests/test_assets/assignments/groups.testdata';
|
||||||
|
import { Group } from '../src/entities/assignments/group.entity';
|
||||||
|
import { makeTestTeacherInvitations } from '../tests/test_assets/classes/teacher-invitations.testdata';
|
||||||
|
import { makeTestClassJoinRequests } from '../tests/test_assets/classes/class-join-requests.testdata';
|
||||||
|
import { makeTestAttachments } from '../tests/test_assets/content/attachments.testdata';
|
||||||
|
import { makeTestQuestions } from '../tests/test_assets/questions/questions.testdata';
|
||||||
|
import { makeTestAnswers } from '../tests/test_assets/questions/answers.testdata';
|
||||||
|
import { makeTestSubmissions } from '../tests/test_assets/assignments/submission.testdata';
|
||||||
|
import { getLogger } from '../src/logging/initalize';
|
||||||
|
|
||||||
|
export async function seedORM(orm: MikroORM): Promise<void> {
|
||||||
|
const logger = getLogger();
|
||||||
|
|
||||||
|
logger.debug('Clearing database...');
|
||||||
|
await orm.schema.clearDatabase();
|
||||||
|
|
||||||
|
logger.debug('Forking entity manager...');
|
||||||
|
const em = forkEntityManager();
|
||||||
|
|
||||||
|
logger.debug('Seeding database...');
|
||||||
|
|
||||||
|
const students = makeTestStudents(em);
|
||||||
|
const teachers = makeTestTeachers(em);
|
||||||
|
const learningObjects = makeTestLearningObjects(em);
|
||||||
|
const learningPaths = makeTestLearningPaths(em);
|
||||||
|
const classes = makeTestClasses(em);
|
||||||
|
const assignments = makeTestAssignemnts(em);
|
||||||
|
|
||||||
|
const groups = makeTestGroups(em);
|
||||||
|
|
||||||
|
assignments[0].groups = new Collection<Group>([getTestGroup01(), getTestGroup02(), getTestGroup03()]);
|
||||||
|
assignments[1].groups = new Collection<Group>([getTestGroup04()]);
|
||||||
|
|
||||||
|
const teacherInvitations = makeTestTeacherInvitations(em);
|
||||||
|
const classJoinRequests = makeTestClassJoinRequests(em);
|
||||||
|
const attachments = makeTestAttachments(em);
|
||||||
|
|
||||||
|
learningObjects[1].attachments = attachments;
|
||||||
|
|
||||||
|
const questions = makeTestQuestions(em);
|
||||||
|
const answers = makeTestAnswers(em);
|
||||||
|
const submissions = makeTestSubmissions(em);
|
||||||
|
|
||||||
|
// Persist all entities
|
||||||
|
await em.persistAndFlush([
|
||||||
|
...students,
|
||||||
|
...teachers,
|
||||||
|
...learningObjects,
|
||||||
|
...learningPaths,
|
||||||
|
...classes,
|
||||||
|
...assignments,
|
||||||
|
...groups,
|
||||||
|
...teacherInvitations,
|
||||||
|
...classJoinRequests,
|
||||||
|
...attachments,
|
||||||
|
...questions,
|
||||||
|
...answers,
|
||||||
|
...submissions,
|
||||||
|
]);
|
||||||
|
|
||||||
|
logger.info('Development database seeded successfully!');
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ import { errorHandler } from '../src/middleware/error-handling/error-handler.js'
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
import cors from '../src/middleware/cors';
|
import cors from '../src/middleware/cors';
|
||||||
import { authenticateUser } from '../src/middleware/auth/auth';
|
import { authenticateUser } from '../src/middleware/auth/auth';
|
||||||
import { seedORM } from './seed';
|
import { seedORM } from './seedORM';
|
||||||
|
|
||||||
const envFile = '../.env.test';
|
const envFile = '../.env.test';
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,17 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- dwengo_grafana_data:/var/lib/grafana
|
- dwengo_grafana_data:/var/lib/grafana
|
||||||
|
|
||||||
|
caching:
|
||||||
|
image: memcached
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- '11211:11211'
|
||||||
|
command:
|
||||||
|
- --conn-limit=1024
|
||||||
|
- --memory-limit=2048
|
||||||
|
- -I 128m
|
||||||
|
- --threads=4
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
dwengo_grafana_data:
|
dwengo_grafana_data:
|
||||||
dwengo_letsencrypt:
|
dwengo_letsencrypt:
|
||||||
|
|
|
@ -97,6 +97,17 @@ services:
|
||||||
- ./config/grafana/grafana.ini:/etc/grafana/grafana.ini
|
- ./config/grafana/grafana.ini:/etc/grafana/grafana.ini
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
caching:
|
||||||
|
image: memcached
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- '11211:11211'
|
||||||
|
command:
|
||||||
|
- --conn-limit=1024
|
||||||
|
- --memory-limit=2048
|
||||||
|
- -I 128m
|
||||||
|
- --threads=4
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
dwengo_grafana_data:
|
dwengo_grafana_data:
|
||||||
dwengo_loki_data:
|
dwengo_loki_data:
|
||||||
|
|
|
@ -17,7 +17,7 @@ export class AnswerController extends BaseController {
|
||||||
|
|
||||||
constructor(questionId: QuestionId) {
|
constructor(questionId: QuestionId) {
|
||||||
super(
|
super(
|
||||||
`learningObject/${questionId.learningObjectIdentifier.hruid}/:${questionId.learningObjectIdentifier.version}/questions/${questionId.sequenceNumber}/answers`,
|
`learningObject/${questionId.learningObjectIdentifier.hruid}/${questionId.learningObjectIdentifier.version}/questions/${questionId.sequenceNumber}/answers`,
|
||||||
);
|
);
|
||||||
this.loId = questionId.learningObjectIdentifier;
|
this.loId = questionId.learningObjectIdentifier;
|
||||||
this.sequenceNumber = questionId.sequenceNumber;
|
this.sequenceNumber = questionId.sequenceNumber;
|
||||||
|
|
31
frontend/tests/controllers/answers-controller.test.ts
Normal file
31
frontend/tests/controllers/answers-controller.test.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { describe, it, expect, beforeEach } from "vitest";
|
||||||
|
import { AnswerController } from "../../src/controllers/answers";
|
||||||
|
import { Language } from "@dwengo-1/common/util/language";
|
||||||
|
|
||||||
|
describe("AnswerController Tests", () => {
|
||||||
|
let controller: AnswerController;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const loiDTO = {
|
||||||
|
hruid: "u_test_multiple_choice",
|
||||||
|
language: Language.English,
|
||||||
|
version: 1,
|
||||||
|
};
|
||||||
|
const questionId = { learningObjectIdentifier: loiDTO, sequenceNumber: 1 };
|
||||||
|
controller = new AnswerController(questionId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fetch all answers", async () => {
|
||||||
|
const result = await controller.getAll(true);
|
||||||
|
expect(result).toHaveProperty("answers");
|
||||||
|
expect(Array.isArray(result.answers)).toBe(true);
|
||||||
|
expect(result.answers.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fetch an answer by sequencenumber", async () => {
|
||||||
|
const answerNumber = 1; // Example sequence number
|
||||||
|
const result = await controller.getBy(answerNumber);
|
||||||
|
expect(result).toHaveProperty("answer");
|
||||||
|
expect(result.answer).toHaveProperty("sequenceNumber", answerNumber);
|
||||||
|
});
|
||||||
|
});
|
30
frontend/tests/controllers/questions-controller.test.ts
Normal file
30
frontend/tests/controllers/questions-controller.test.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { describe, it, expect, beforeEach } from "vitest";
|
||||||
|
import { Language } from "@dwengo-1/common/util/language";
|
||||||
|
import { QuestionController } from "../../src/controllers/questions";
|
||||||
|
|
||||||
|
describe("QuestionController Tests", () => {
|
||||||
|
let controller: QuestionController;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const loiDTO = {
|
||||||
|
hruid: "u_test_multiple_choice",
|
||||||
|
language: Language.English,
|
||||||
|
version: 1,
|
||||||
|
};
|
||||||
|
controller = new QuestionController(loiDTO);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fetch all questions", async () => {
|
||||||
|
const result = await controller.getAll(true);
|
||||||
|
expect(result).toHaveProperty("questions");
|
||||||
|
expect(Array.isArray(result.questions)).toBe(true);
|
||||||
|
expect(result.questions.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fetch an question by sequencenumber", async () => {
|
||||||
|
const questionNumber = 1; // Example sequence number
|
||||||
|
const result = await controller.getBy(questionNumber);
|
||||||
|
expect(result).toHaveProperty("question");
|
||||||
|
expect(result.question).toHaveProperty("sequenceNumber", questionNumber);
|
||||||
|
});
|
||||||
|
});
|
|
@ -27,13 +27,8 @@ test.each([
|
||||||
{ username: "DireStraits", firstName: "Mark", lastName: "Knopfler" },
|
{ username: "DireStraits", firstName: "Mark", lastName: "Knopfler" },
|
||||||
{ username: "Tool", firstName: "Maynard", lastName: "Keenan" },
|
{ username: "Tool", firstName: "Maynard", lastName: "Keenan" },
|
||||||
{ username: "SmashingPumpkins", firstName: "Billy", lastName: "Corgan" },
|
{ username: "SmashingPumpkins", firstName: "Billy", lastName: "Corgan" },
|
||||||
{ username: "PinkFloyd", firstName: "David", lastName: "Gilmoure" },
|
|
||||||
{ username: "TheDoors", firstName: "Jim", lastName: "Morisson" },
|
|
||||||
// ⚠️ Deze mag niet gebruikt worden in elke test!
|
|
||||||
{ username: "Nirvana", firstName: "Kurt", lastName: "Cobain" },
|
|
||||||
// Makes sure when logged in as leerling1, there exists a corresponding user
|
|
||||||
{ username: "testleerling1", firstName: "Gerald", lastName: "Schmittinger" },
|
{ username: "testleerling1", firstName: "Gerald", lastName: "Schmittinger" },
|
||||||
])("Get classes of student", async (student) => {
|
])("Get classes of student", async (student) => {
|
||||||
const data = await controller.getClasses(student.username, true);
|
const data = await controller.getClasses(student.username, true);
|
||||||
expect(data.classes).to.have.length.greaterThan(0);
|
expect(data.classes).to.have.length.greaterThan(0, `Found no classes for ${student.username}`);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { describe, expect, it } from "vitest";
|
|
||||||
import { SubmissionController } from "../../src/controllers/submissions";
|
|
||||||
import { Language } from "../../src/data-objects/language";
|
|
||||||
|
|
||||||
describe("Test controller submissions", () => {
|
|
||||||
it("Get submission by number", async () => {
|
|
||||||
const hruid = "id03";
|
|
||||||
const classId = "X2J9QT"; // Class01
|
|
||||||
const controller = new SubmissionController(hruid);
|
|
||||||
|
|
||||||
const data = await controller.getByNumber(Language.English, 1, classId, 1, 1, 1);
|
|
||||||
|
|
||||||
expect(data.submission).to.have.property("submissionNumber");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process";
|
||||||
import { ChildProcess, spawnSync } from "node:child_process";
|
import { ChildProcess, spawnSync } from "node:child_process";
|
||||||
|
import { getLogger } from "../../backend/src/logging/initalize";
|
||||||
|
|
||||||
let backendProcess: ChildProcess;
|
let backendProcess: ChildProcess;
|
||||||
|
|
||||||
|
@ -35,6 +36,8 @@ export async function setup(): Promise<void> {
|
||||||
|
|
||||||
export async function teardown(): Promise<void> {
|
export async function teardown(): Promise<void> {
|
||||||
if (backendProcess) {
|
if (backendProcess) {
|
||||||
backendProcess.kill();
|
while (!backendProcess.kill()) {
|
||||||
|
getLogger().error(`Failed to kill backend process! Retrying...`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
389
package-lock.json
generated
389
package-lock.json
generated
|
@ -51,6 +51,7 @@
|
||||||
"jwks-rsa": "^3.1.0",
|
"jwks-rsa": "^3.1.0",
|
||||||
"loki-logger-ts": "^1.0.2",
|
"loki-logger-ts": "^1.0.2",
|
||||||
"marked": "^15.0.7",
|
"marked": "^15.0.7",
|
||||||
|
"memjs": "^1.3.2",
|
||||||
"mime-types": "^3.0.1",
|
"mime-types": "^3.0.1",
|
||||||
"nanoid": "^5.1.5",
|
"nanoid": "^5.1.5",
|
||||||
"response-time": "^2.3.3",
|
"response-time": "^2.3.3",
|
||||||
|
@ -66,6 +67,7 @@
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/express-fileupload": "^1.5.1",
|
"@types/express-fileupload": "^1.5.1",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
"@types/memjs": "^1.3.3",
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.13.4",
|
||||||
"@types/response-time": "^2.3.8",
|
"@types/response-time": "^2.3.8",
|
||||||
|
@ -78,6 +80,19 @@
|
||||||
"vitest": "^3.0.6"
|
"vitest": "^3.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"backend/node_modules/globals": {
|
||||||
|
"version": "15.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
||||||
|
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"name": "@dwengo-1/common",
|
"name": "@dwengo-1/common",
|
||||||
"version": "0.2.0"
|
"version": "0.2.0"
|
||||||
|
@ -753,9 +768,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/search": {
|
"node_modules/@codemirror/search": {
|
||||||
"version": "6.5.10",
|
"version": "6.5.11",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.10.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz",
|
||||||
"integrity": "sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==",
|
"integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
|
@ -1492,9 +1507,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/core": {
|
"node_modules/@eslint/core": {
|
||||||
"version": "0.13.0",
|
"version": "0.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
|
||||||
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
|
"integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1539,19 +1554,6 @@
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc/node_modules/globals": {
|
|
||||||
"version": "14.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
|
||||||
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@eslint/eslintrc/node_modules/ignore": {
|
"node_modules/@eslint/eslintrc/node_modules/ignore": {
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||||
|
@ -1576,13 +1578,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.26.0",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
|
||||||
"integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==",
|
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://eslint.org/donate"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/object-schema": {
|
"node_modules/@eslint/object-schema": {
|
||||||
|
@ -1596,13 +1601,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/plugin-kit": {
|
"node_modules/@eslint/plugin-kit": {
|
||||||
"version": "0.2.8",
|
"version": "0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz",
|
||||||
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
|
"integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/core": "^0.13.0",
|
"@eslint/core": "^0.14.0",
|
||||||
"levn": "^0.4.1"
|
"levn": "^0.4.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2075,28 +2080,6 @@
|
||||||
"@mikro-orm/core": "^6.0.0"
|
"@mikro-orm/core": "^6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@modelcontextprotocol/sdk": {
|
|
||||||
"version": "1.11.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.2.tgz",
|
|
||||||
"integrity": "sha512-H9vwztj5OAqHg9GockCQC06k1natgcxWQSRpQcPJf6i5+MWBzfKkRtxGbjQf0X2ihii0ffLZCRGbYV2f2bjNCQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"content-type": "^1.0.5",
|
|
||||||
"cors": "^2.8.5",
|
|
||||||
"cross-spawn": "^7.0.3",
|
|
||||||
"eventsource": "^3.0.2",
|
|
||||||
"express": "^5.0.1",
|
|
||||||
"express-rate-limit": "^7.5.0",
|
|
||||||
"pkce-challenge": "^5.0.0",
|
|
||||||
"raw-body": "^3.0.0",
|
|
||||||
"zod": "^3.23.8",
|
|
||||||
"zod-to-json-schema": "^3.24.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@napi-rs/snappy-android-arm-eabi": {
|
"node_modules/@napi-rs/snappy-android-arm-eabi": {
|
||||||
"version": "7.2.2",
|
"version": "7.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm-eabi/-/snappy-android-arm-eabi-7.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm-eabi/-/snappy-android-arm-eabi-7.2.2.tgz",
|
||||||
|
@ -3107,6 +3090,16 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/memjs": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/memjs/-/memjs-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-OqZuZ2T0ErrygA/WUqQjmsB1GpxAt84OCgKPKbsSWwYKA/oe/lTiqvqMgFOv/Ngl8p+nytgdhrdxJtikOgimCg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||||
|
@ -3127,9 +3120,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.15.17",
|
"version": "22.15.18",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz",
|
||||||
"integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==",
|
"integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
|
@ -3611,30 +3604,30 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@volar/language-core": {
|
"node_modules/@volar/language-core": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.14.tgz",
|
||||||
"integrity": "sha512-MnQJ7eKchJx5Oz+YdbqyFUk8BN6jasdJv31n/7r6/WwlOOv7qzvot6B66887l2ST3bUW4Mewml54euzpJWA6bg==",
|
"integrity": "sha512-X6beusV0DvuVseaOEy7GoagS4rYHgDHnTrdOj5jeUb49fW5ceQyP9Ej5rBhqgz2wJggl+2fDbbojq1XKaxDi6w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@volar/source-map": "2.4.13"
|
"@volar/source-map": "2.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@volar/source-map": {
|
"node_modules/@volar/source-map": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.14.tgz",
|
||||||
"integrity": "sha512-l/EBcc2FkvHgz2ZxV+OZK3kMSroMr7nN3sZLF2/f6kWW66q8+tEL4giiYyFjt0BcubqJhBt6soYIrAPhg/Yr+Q==",
|
"integrity": "sha512-5TeKKMh7Sfxo8021cJfmBzcjfY1SsXsPMMjMvjY7ivesdnybqqS+GxGAoXHAOUawQTwtdUxgP65Im+dEmvWtYQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@volar/typescript": {
|
"node_modules/@volar/typescript": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.14.tgz",
|
||||||
"integrity": "sha512-Ukz4xv84swJPupZeoFsQoeJEOm7U9pqsEnaGGgt5ni3SCTa22m8oJP5Nng3Wed7Uw5RBELdLxxORX8YhJPyOgQ==",
|
"integrity": "sha512-p8Z6f/bZM3/HyCdRNFZOEEzts51uV8WHeN8Tnfnm2EBv6FDB2TQLzfVx7aJvnl8ofKAOnS64B2O8bImBFaauRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@volar/language-core": "2.4.13",
|
"@volar/language-core": "2.4.14",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"vscode-uri": "^3.0.8"
|
"vscode-uri": "^3.0.8"
|
||||||
}
|
}
|
||||||
|
@ -3693,16 +3686,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-core": {
|
"node_modules/@vue/compiler-core": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.14.tgz",
|
||||||
"integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
|
"integrity": "sha512-k7qMHMbKvoCXIxPhquKQVw3Twid3Kg4s7+oYURxLGRd56LiuHJVrvFKI4fm2AM3c8apqODPfVJGoh8nePbXMRA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.25.3",
|
"@babel/parser": "^7.27.2",
|
||||||
"@vue/shared": "3.5.13",
|
"@vue/shared": "3.5.14",
|
||||||
"entities": "^4.5.0",
|
"entities": "^4.5.0",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
"source-map-js": "^1.2.0"
|
"source-map-js": "^1.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-core/node_modules/entities": {
|
"node_modules/@vue/compiler-core/node_modules/entities": {
|
||||||
|
@ -3718,40 +3711,40 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-dom": {
|
"node_modules/@vue/compiler-dom": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.14.tgz",
|
||||||
"integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
|
"integrity": "sha512-1aOCSqxGOea5I80U2hQJvXYpPm/aXo95xL/m/mMhgyPUsKe9jhjwWpziNAw7tYRnbz1I61rd9Mld4W9KmmRoug==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-core": "3.5.13",
|
"@vue/compiler-core": "3.5.14",
|
||||||
"@vue/shared": "3.5.13"
|
"@vue/shared": "3.5.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-sfc": {
|
"node_modules/@vue/compiler-sfc": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.14.tgz",
|
||||||
"integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
|
"integrity": "sha512-9T6m/9mMr81Lj58JpzsiSIjBgv2LiVoWjIVa7kuXHICUi8LiDSIotMpPRXYJsXKqyARrzjT24NAwttrMnMaCXA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.25.3",
|
"@babel/parser": "^7.27.2",
|
||||||
"@vue/compiler-core": "3.5.13",
|
"@vue/compiler-core": "3.5.14",
|
||||||
"@vue/compiler-dom": "3.5.13",
|
"@vue/compiler-dom": "3.5.14",
|
||||||
"@vue/compiler-ssr": "3.5.13",
|
"@vue/compiler-ssr": "3.5.14",
|
||||||
"@vue/shared": "3.5.13",
|
"@vue/shared": "3.5.14",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
"magic-string": "^0.30.11",
|
"magic-string": "^0.30.17",
|
||||||
"postcss": "^8.4.48",
|
"postcss": "^8.5.3",
|
||||||
"source-map-js": "^1.2.0"
|
"source-map-js": "^1.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-ssr": {
|
"node_modules/@vue/compiler-ssr": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.14.tgz",
|
||||||
"integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
|
"integrity": "sha512-Y0G7PcBxr1yllnHuS/NxNCSPWnRGH4Ogrp0tsLA5QemDZuJLs99YjAKQ7KqkHE0vCg4QTKlQzXLKCMF7WPSl7Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.13",
|
"@vue/compiler-dom": "3.5.14",
|
||||||
"@vue/shared": "3.5.13"
|
"@vue/shared": "3.5.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-vue2": {
|
"node_modules/@vue/compiler-vue2": {
|
||||||
|
@ -3882,53 +3875,53 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/reactivity": {
|
"node_modules/@vue/reactivity": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.14.tgz",
|
||||||
"integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
|
"integrity": "sha512-7cK1Hp343Fu/SUCCO52vCabjvsYu7ZkOqyYu7bXV9P2yyfjUMUXHZafEbq244sP7gf+EZEz+77QixBTuEqkQQw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/shared": "3.5.13"
|
"@vue/shared": "3.5.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-core": {
|
"node_modules/@vue/runtime-core": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.14.tgz",
|
||||||
"integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
|
"integrity": "sha512-w9JWEANwHXNgieAhxPpEpJa+0V5G0hz3NmjAZwlOebtfKyp2hKxKF0+qSh0Xs6/PhfGihuSdqMprMVcQU/E6ag==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.13",
|
"@vue/reactivity": "3.5.14",
|
||||||
"@vue/shared": "3.5.13"
|
"@vue/shared": "3.5.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/runtime-dom": {
|
"node_modules/@vue/runtime-dom": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.14.tgz",
|
||||||
"integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
|
"integrity": "sha512-lCfR++IakeI35TVR80QgOelsUIdcKjd65rWAMfdSlCYnaEY5t3hYwru7vvcWaqmrK+LpI7ZDDYiGU5V3xjMacw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.13",
|
"@vue/reactivity": "3.5.14",
|
||||||
"@vue/runtime-core": "3.5.13",
|
"@vue/runtime-core": "3.5.14",
|
||||||
"@vue/shared": "3.5.13",
|
"@vue/shared": "3.5.14",
|
||||||
"csstype": "^3.1.3"
|
"csstype": "^3.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/server-renderer": {
|
"node_modules/@vue/server-renderer": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.14.tgz",
|
||||||
"integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
|
"integrity": "sha512-Rf/ISLqokIvcySIYnv3tNWq40PLpNLDLSJwwVWzG6MNtyIhfbcrAxo5ZL9nARJhqjZyWWa40oRb2IDuejeuv6w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-ssr": "3.5.13",
|
"@vue/compiler-ssr": "3.5.14",
|
||||||
"@vue/shared": "3.5.13"
|
"@vue/shared": "3.5.14"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/shared": {
|
"node_modules/@vue/shared": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.14.tgz",
|
||||||
"integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==",
|
"integrity": "sha512-oXTwNxVfc9EtP1zzXAlSlgARLXNC84frFYkS0HHz0h3E4WZSP9sywqjqzGCP9Y34M8ipNmd380pVgmMuwELDyQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@vue/test-utils": {
|
"node_modules/@vue/test-utils": {
|
||||||
|
@ -3962,14 +3955,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vueuse/core": {
|
"node_modules/@vueuse/core": {
|
||||||
"version": "13.1.0",
|
"version": "13.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.2.0.tgz",
|
||||||
"integrity": "sha512-PAauvdRXZvTWXtGLg8cPUFjiZEddTqmogdwYpnn60t08AA5a8Q4hZokBnpTOnVNqySlFlTcRYIC8OqreV4hv3Q==",
|
"integrity": "sha512-n5TZoIAxbWAQ3PqdVPDzLgIRQOujFfMlatdI+f7ditSmoEeNpPBvp7h2zamzikCmrhFIePAwdEQB6ENccHr7Rg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/web-bluetooth": "^0.0.21",
|
"@types/web-bluetooth": "^0.0.21",
|
||||||
"@vueuse/metadata": "13.1.0",
|
"@vueuse/metadata": "13.2.0",
|
||||||
"@vueuse/shared": "13.1.0"
|
"@vueuse/shared": "13.2.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/antfu"
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
@ -3979,18 +3972,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vueuse/metadata": {
|
"node_modules/@vueuse/metadata": {
|
||||||
"version": "13.1.0",
|
"version": "13.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.2.0.tgz",
|
||||||
"integrity": "sha512-+TDd7/a78jale5YbHX9KHW3cEDav1lz1JptwDvep2zSG8XjCsVE+9mHIzjTOaPbHUAk5XiE4jXLz51/tS+aKQw==",
|
"integrity": "sha512-kPpzuQCU0+D8DZCzK0iPpIcXI+6ufWSgwnjJ6//GNpEn+SHViaCtR+XurzORChSgvpHO9YC8gGM97Y1kB+UabA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/antfu"
|
"url": "https://github.com/sponsors/antfu"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vueuse/shared": {
|
"node_modules/@vueuse/shared": {
|
||||||
"version": "13.1.0",
|
"version": "13.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.2.0.tgz",
|
||||||
"integrity": "sha512-IVS/qRRjhPTZ6C2/AM3jieqXACGwFZwWTdw5sNTSKk2m/ZpkuuN+ri+WCVUP8TqaKwJYt/KuMwmXspMAw8E6ew==",
|
"integrity": "sha512-vx9ZPDF5HcU9up3Jgt3G62dMUfZEdk6tLyBAHYAG4F4n73vpaA7J5hdncDI/lS9Vm7GA/FPlbOmh9TrDZROTpg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/antfu"
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
@ -5448,9 +5441,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.152",
|
"version": "1.5.155",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.152.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
|
||||||
"integrity": "sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==",
|
"integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
@ -5656,9 +5649,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.26.0",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
|
||||||
"integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==",
|
"integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -5666,14 +5659,13 @@
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
"@eslint/config-array": "^0.20.0",
|
"@eslint/config-array": "^0.20.0",
|
||||||
"@eslint/config-helpers": "^0.2.1",
|
"@eslint/config-helpers": "^0.2.1",
|
||||||
"@eslint/core": "^0.13.0",
|
"@eslint/core": "^0.14.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "9.26.0",
|
"@eslint/js": "9.27.0",
|
||||||
"@eslint/plugin-kit": "^0.2.8",
|
"@eslint/plugin-kit": "^0.3.1",
|
||||||
"@humanfs/node": "^0.16.6",
|
"@humanfs/node": "^0.16.6",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@humanwhocodes/retry": "^0.4.2",
|
"@humanwhocodes/retry": "^0.4.2",
|
||||||
"@modelcontextprotocol/sdk": "^1.8.0",
|
|
||||||
"@types/estree": "^1.0.6",
|
"@types/estree": "^1.0.6",
|
||||||
"@types/json-schema": "^7.0.15",
|
"@types/json-schema": "^7.0.15",
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
|
@ -5697,8 +5689,7 @@
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"minimatch": "^3.1.2",
|
"minimatch": "^3.1.2",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
"optionator": "^0.9.3",
|
"optionator": "^0.9.3"
|
||||||
"zod": "^3.24.2"
|
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"eslint": "bin/eslint.js"
|
"eslint": "bin/eslint.js"
|
||||||
|
@ -6105,29 +6096,6 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eventsource": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"eventsource-parser": "^3.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eventsource-parser": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/execa": {
|
"node_modules/execa": {
|
||||||
"version": "9.5.3",
|
"version": "9.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/execa/-/execa-9.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/execa/-/execa-9.5.3.tgz",
|
||||||
|
@ -6242,22 +6210,6 @@
|
||||||
"node": ">= 8.0.0"
|
"node": ">= 8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express-rate-limit": {
|
|
||||||
"version": "7.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
|
|
||||||
"integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/express-rate-limit"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/express-unless": {
|
"node_modules/express-unless": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz",
|
||||||
|
@ -6861,9 +6813,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globals": {
|
"node_modules/globals": {
|
||||||
"version": "15.15.0",
|
"version": "14.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
||||||
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -8354,6 +8306,15 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/memjs": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/memjs/-/memjs-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-qUEg2g8vxPe+zPn09KidjIStHPtoBO8Cttm8bgJFWWabbsjQ9Av9Ky+6UcvKx6ue0LLb/LEhtcyQpRyKfzeXcg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/memoize-one": {
|
"node_modules/memoize-one": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||||
|
@ -9504,16 +9465,6 @@
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pkce-challenge": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.20.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/playwright": {
|
"node_modules/playwright": {
|
||||||
"version": "1.50.1",
|
"version": "1.50.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz",
|
||||||
|
@ -9759,9 +9710,9 @@
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/protobufjs": {
|
"node_modules/protobufjs": {
|
||||||
"version": "7.5.1",
|
"version": "7.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.2.tgz",
|
||||||
"integrity": "sha512-3qx3IRjR9WPQKagdwrKjO3Gu8RgQR2qqw+1KnigWhoVjFqegIj1K3bP11sGqhxrO46/XL7lekuG4jmjL+4cLsw==",
|
"integrity": "sha512-f2ls6rpO6G153Cy+o2XQ+Y0sARLOZ17+OGVLHrc3VUKcLHYKEKWbkSujdBWQXM7gKn5NTfp0XnRPZn1MIu8n9w==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -10966,9 +10917,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte": {
|
"node_modules/svelte": {
|
||||||
"version": "5.28.6",
|
"version": "5.30.1",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.28.6.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.30.1.tgz",
|
||||||
"integrity": "sha512-9qqr7mw8YR9PAnxGFfzCK6PUlNGtns7wVavrhnxyf3fpB1mP/Ol55Z2UnIapsSzNNl3k9qw7cZ22PdE8+xT/jQ==",
|
"integrity": "sha512-QIYtKnJGkubWXtNkrUBKVCvyo9gjcccdbnvXfwsGNhvbeNNdQjRDTa/BiQcJ2kWXbXPQbWKyT7CUu53KIj1rfw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ampproject/remapping": "^2.3.0",
|
"@ampproject/remapping": "^2.3.0",
|
||||||
|
@ -11093,13 +11044,13 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/synckit": {
|
"node_modules/synckit": {
|
||||||
"version": "0.11.4",
|
"version": "0.11.5",
|
||||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz",
|
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.5.tgz",
|
||||||
"integrity": "sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ==",
|
"integrity": "sha512-frqvfWyDA5VPVdrWfH24uM6SI/O8NLpVbIIJxb8t/a3YGsp4AW9CYgSKC0OaSEfexnp7Y1pVh2Y6IHO8ggGDmA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pkgr/core": "^0.2.3",
|
"@pkgr/core": "^0.2.4",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -12118,16 +12069,16 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vue": {
|
"node_modules/vue": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.14.tgz",
|
||||||
"integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
|
"integrity": "sha512-LbOm50/vZFG6Mhy6KscQYXZMQ0LMCC/y40HDJPPvGFQ+i/lUH+PJHR6C3assgOQiXdl6tAfsXHbXYVBZZu65ew==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.13",
|
"@vue/compiler-dom": "3.5.14",
|
||||||
"@vue/compiler-sfc": "3.5.13",
|
"@vue/compiler-sfc": "3.5.14",
|
||||||
"@vue/runtime-dom": "3.5.13",
|
"@vue/runtime-dom": "3.5.14",
|
||||||
"@vue/server-renderer": "3.5.13",
|
"@vue/server-renderer": "3.5.14",
|
||||||
"@vue/shared": "3.5.13"
|
"@vue/shared": "3.5.14"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
|
@ -12262,9 +12213,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vuetify": {
|
"node_modules/vuetify": {
|
||||||
"version": "3.8.4",
|
"version": "3.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.8.4.tgz",
|
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.8.5.tgz",
|
||||||
"integrity": "sha512-hfA1eqA6vhrF5LF8Yfk0uHdNUmh8Uckxn5wREiThO82HW/9Vfreh+IpxPgEtCsAhV33KW+NVamltQCu3HczRKw==",
|
"integrity": "sha512-W/mTaNDyO6NRqAQmnkMUn9TYvRb//BPF/vk7h3+2xNJOyI9ev90JmYjrihOtb+6QDrB79wVUH0Y+0OjYK73GsA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20 || >=14.13"
|
"node": "^12.20 || >=14.13"
|
||||||
|
@ -12819,26 +12770,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz",
|
||||||
"integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
|
"integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
|
||||||
"node_modules/zod": {
|
|
||||||
"version": "3.24.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz",
|
|
||||||
"integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/zod-to-json-schema": {
|
|
||||||
"version": "3.24.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz",
|
|
||||||
"integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"peerDependencies": {
|
|
||||||
"zod": "^3.24.1"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue