Files
3dtours/backend/tests/sceneIntegration.test.js
T

138 lines
5.0 KiB
JavaScript

const request = require('supertest');
const mongoose = require('mongoose');
const fs = require('fs');
const path = require('path');
const Scene = require('../models/Scene');
const Asset = require('../models/Asset');
const Hotspot = require('../models/Hotspot');
const User = require('../models/User');
// Mock fs để không xóa file thật trong quá trình test và kiểm tra số lần gọi hàm
jest.mock('fs', () => ({
...jest.requireActual('fs'),
promises: {
unlink: jest.fn().mockResolvedValue()
},
existsSync: jest.fn().mockReturnValue(true)
}));
// Import app - Giả định server.js của bạn export express app
// Nếu file khởi tạo app của bạn có tên khác, hãy điều chỉnh đường dẫn bên dưới
const app = require('../server');
describe('Integration Test: Cascade Scene Deletion (BFS)', () => {
let adminToken;
let adminUser;
let parentAsset, childAsset;
let parentScene, childScene;
beforeAll(async () => {
// Kết nối tới Database Test (Sử dụng biến môi trường hoặc mặc định)
if (mongoose.connection.readyState === 0) {
await mongoose.connect(process.env.MONGO_URI || 'mongodb://localhost:27017/3dtours_test');
}
// Thiết lập Admin User để thực hiện các request có quyền bảo mật
await User.deleteMany({});
adminUser = await User.create({
fullName: 'Admin Test',
username: 'admintest',
email: 'admin@test.com',
password: 'password123',
role: 'admin',
agreedToRules: true
});
// Đăng nhập để lấy JWT Token
const res = await request(app)
.post('/api/auth/login')
.send({ username: 'admintest', password: 'password123' });
adminToken = res.body.token;
});
afterAll(async () => {
await User.deleteMany({});
await Scene.deleteMany({});
await Asset.deleteMany({});
await Hotspot.deleteMany({});
await mongoose.connection.close();
});
beforeEach(async () => {
jest.clearAllMocks();
await Scene.deleteMany({});
await Asset.deleteMany({});
await Hotspot.deleteMany({});
// 1. Tạo dữ liệu Scene Cha và Asset tương ứng
parentAsset = await Asset.create({
filePath: path.join(__dirname, '../uploads/parent_room.jpg'),
fileSize: 1024 * 1024,
uploadedBy: adminUser._id
});
parentScene = await Scene.create({
name: 'Phòng Khách (Cha)',
assetId: parentAsset._id,
createdBy: adminUser._id,
status: 'completed'
});
// 2. Tạo dữ liệu Scene Con và Asset tương ứng
childAsset = await Asset.create({
filePath: path.join(__dirname, '../uploads/child_balcony.jpg'),
fileSize: 800 * 1024,
uploadedBy: adminUser._id
});
childScene = await Scene.create({
name: 'Ban Công (Con)',
assetId: childAsset._id,
createdBy: adminUser._id,
status: 'completed'
});
// 3. Tạo liên kết: Cha -> trỏ tới -> Con thông qua Hotspot
await Hotspot.create({
parent_scene_id: parentScene._id,
target_scene_id: childScene._id,
title: 'Đi ra Ban Công'
});
});
test('Khi xóa scene CHA, phải xóa dây chuyền sang scene CON và gỡ bỏ toàn bộ file vật lý', async () => {
const res = await request(app)
.delete(`/api/scenes/${parentScene._id}`)
.set('Authorization', `Bearer ${adminToken}`);
expect(res.status).toBe(200);
// Kiểm tra Database: Không còn bất kỳ scene nào
const scenesInDB = await Scene.find({});
expect(scenesInDB.length).toBe(0);
// Kiểm tra Assets: Các bản ghi asset cũng phải bị xóa sạch
const assetsInDB = await Asset.find({});
expect(assetsInDB.length).toBe(0);
// Kiểm tra Filesystem: Phải gọi lệnh xóa (unlink) cho cả 2 tệp tin (cha và con)
expect(fs.promises.unlink).toHaveBeenCalledTimes(2);
});
test('Khi xóa scene CON, scene CHA vẫn phải tồn tại (Không được xóa ngược)', async () => {
const res = await request(app)
.delete(`/api/scenes/${childScene._id}`)
.set('Authorization', `Bearer ${adminToken}`);
expect(res.status).toBe(200);
// Scene Cha và Asset của nó phải còn nguyên trong Database
const parentInDB = await Scene.findById(parentScene._id);
expect(parentInDB).not.toBeNull();
const parentAssetInDB = await Asset.findById(parentAsset._id);
expect(parentAssetInDB).not.toBeNull();
// Chỉ có 1 tệp tin bị xóa (tệp của scene con)
expect(fs.promises.unlink).toHaveBeenCalledTimes(1);
expect(fs.promises.unlink).toHaveBeenCalledWith(expect.stringContaining('child_balcony.jpg'));
});
});