feat: QR 자산 스캔 점검, 모바일 웹뷰 및 관리자 승인 시스템 구현 (DB 기반 맵 좌표 저장 단일화 포함)
This commit is contained in:
231
scratch/test_audit.cjs
Normal file
231
scratch/test_audit.cjs
Normal file
@@ -0,0 +1,231 @@
|
||||
const assert = require('assert');
|
||||
const http = require('http');
|
||||
const mysql = require('mysql2/promise');
|
||||
require('dotenv').config();
|
||||
|
||||
const BASE_URL = 'http://localhost:3001';
|
||||
|
||||
function request(method, path, body = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = `${BASE_URL}${path}`;
|
||||
const options = {
|
||||
method: method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
const req = http.request(url, options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => { data += chunk; });
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
resolve({ status: res.statusCode, body: parsed });
|
||||
} catch (e) {
|
||||
resolve({ status: res.statusCode, body: data });
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', (err) => reject(err));
|
||||
if (body) {
|
||||
req.write(JSON.stringify(body));
|
||||
}
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
console.log('🧪 Starting Audit TDD Tests...');
|
||||
const pool = mysql.createPool({
|
||||
host: process.env.DB_HOST,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASS,
|
||||
database: process.env.DB_NAME,
|
||||
port: process.env.DB_PORT
|
||||
});
|
||||
|
||||
const connection = await pool.getConnection();
|
||||
|
||||
try {
|
||||
// Clean up any test records
|
||||
console.log('🧹 Cleaning up test records...');
|
||||
await connection.query("DELETE FROM asset_audit_pending WHERE asset_code LIKE 'TEST-ASSET-%'");
|
||||
|
||||
// Check if test assets exist in asset_core & asset_location
|
||||
// We will use an existing asset or insert a dummy test asset
|
||||
const [testAssets] = await connection.query("SELECT id FROM asset_core WHERE asset_code = 'TEST-ASSET-001'");
|
||||
let testAssetId;
|
||||
if (testAssets.length === 0) {
|
||||
console.log('⏳ Inserting dummy test asset...');
|
||||
testAssetId = 'test_asset_uuid_123456';
|
||||
await connection.query(`
|
||||
INSERT INTO asset_core (id, asset_code, category, asset_type, asset_purpose)
|
||||
VALUES (?, 'TEST-ASSET-001', 'server', 'Server', 'TDD Test Server')
|
||||
`, [testAssetId]);
|
||||
await connection.query(`
|
||||
INSERT INTO asset_location (asset_id, location, location_detail, location_photo, loc_x, loc_y, is_active)
|
||||
VALUES (?, 'Initial Location', 'Initial Detail', 'initial.png', '10.00', '10.00', 1)
|
||||
`, [testAssetId]);
|
||||
} else {
|
||||
testAssetId = testAssets[0].id;
|
||||
}
|
||||
|
||||
// 1. Test GET /api/physical-locations
|
||||
console.log('👉 Test 1: GET /api/physical-locations');
|
||||
const res1 = await request('GET', '/api/physical-locations');
|
||||
assert.strictEqual(res1.status, 200, 'GET /api/physical-locations should return 200');
|
||||
assert(Array.isArray(res1.body), 'Response should be an array of physical locations');
|
||||
assert(res1.body.length > 0, 'Should return at least one physical location');
|
||||
console.log(`✅ Test 1 Passed: Found ${res1.body.length} physical locations.`);
|
||||
|
||||
const sampleLocation = res1.body[0].location_code;
|
||||
|
||||
// 2. Test POST /api/audit/scan
|
||||
console.log(`👉 Test 2: POST /api/audit/scan (Location: ${sampleLocation}, Asset: TEST-ASSET-001)`);
|
||||
const res2 = await request('POST', '/api/audit/scan', {
|
||||
asset_code: 'TEST-ASSET-001',
|
||||
physical_location_code: sampleLocation
|
||||
});
|
||||
assert.strictEqual(res2.status, 200, 'POST /api/audit/scan should return 200');
|
||||
assert.strictEqual(res2.body.success, true, 'Response success should be true');
|
||||
assert(res2.body.pending_id, 'Response should contain pending_id');
|
||||
console.log(`✅ Test 2 Passed: Pending scan registered with ID: ${res2.body.pending_id}`);
|
||||
|
||||
const pendingId = res2.body.pending_id;
|
||||
|
||||
// 3. Test GET /api/audit/pending
|
||||
console.log('👉 Test 3: GET /api/audit/pending');
|
||||
const res3 = await request('GET', '/api/audit/pending');
|
||||
assert.strictEqual(res3.status, 200, 'GET /api/audit/pending should return 200');
|
||||
assert(Array.isArray(res3.body), 'Response should be an array');
|
||||
const pendingItem = res3.body.find(item => item.id === pendingId);
|
||||
assert(pendingItem, 'Pending list should contain the newly registered scan');
|
||||
assert.strictEqual(pendingItem.asset_code, 'TEST-ASSET-001', 'Asset code should match');
|
||||
assert.strictEqual(pendingItem.physical_location_code, sampleLocation, 'Location code should match');
|
||||
assert.strictEqual(pendingItem.status, 'PENDING', 'Status should be PENDING');
|
||||
console.log('✅ Test 3 Passed: Newly registered scan found in pending list with correct details.');
|
||||
|
||||
// 4. Test POST /api/audit/approve
|
||||
console.log(`👉 Test 4: POST /api/audit/approve (Pending ID: ${pendingId})`);
|
||||
const res4 = await request('POST', '/api/audit/approve', {
|
||||
pending_ids: [pendingId],
|
||||
processed_by: 'TDD-TESTER'
|
||||
});
|
||||
assert.strictEqual(res4.status, 200, 'POST /api/audit/approve should return 200');
|
||||
assert.strictEqual(res4.body.success, true, 'Response success should be true');
|
||||
console.log('✅ Test 4 Passed: Audit approved.');
|
||||
|
||||
// Verify database updates
|
||||
console.log('🔍 Verifying updates in database...');
|
||||
const [pendingCheck] = await connection.query(
|
||||
'SELECT status, processed_by FROM asset_audit_pending WHERE id = ?',
|
||||
[pendingId]
|
||||
);
|
||||
assert.strictEqual(pendingCheck[0].status, 'APPROVED', 'Pending record status should be APPROVED');
|
||||
assert.strictEqual(pendingCheck[0].processed_by, 'TDD-TESTER', 'Processed by should match');
|
||||
|
||||
const [locationCheck] = await connection.query(
|
||||
'SELECT physical_location_code, location_photo, loc_x, loc_y FROM asset_location WHERE asset_id = ? AND is_active = 1',
|
||||
[testAssetId]
|
||||
);
|
||||
const [physLoc] = await connection.query(
|
||||
'SELECT map_image, map_x, map_y FROM physical_locations WHERE location_code = ?',
|
||||
[sampleLocation]
|
||||
);
|
||||
assert.strictEqual(locationCheck[0].physical_location_code, sampleLocation, 'Asset location code should be updated');
|
||||
assert.strictEqual(locationCheck[0].location_photo, physLoc[0].map_image, 'Asset map_image should be updated');
|
||||
assert.strictEqual(parseFloat(locationCheck[0].loc_x).toFixed(2), parseFloat(physLoc[0].map_x).toFixed(2), 'Asset map_x should be updated');
|
||||
assert.strictEqual(parseFloat(locationCheck[0].loc_y).toFixed(2), parseFloat(physLoc[0].map_y).toFixed(2), 'Asset map_y should be updated');
|
||||
console.log('✅ Database verification passed: Asset location and map coordinates updated successfully!');
|
||||
|
||||
// 5. Test GET /api/maps (Before modification)
|
||||
console.log('👉 Test 5: GET /api/maps');
|
||||
const res5 = await request('GET', '/api/maps');
|
||||
assert.strictEqual(res5.status, 200, 'GET /api/maps should return 200');
|
||||
assert(typeof res5.body === 'object' && res5.body !== null, 'Response should be a map config object');
|
||||
console.log('✅ Test 5 Passed: GET /api/maps returned valid object.');
|
||||
|
||||
// 6. Test POST /api/maps/save
|
||||
console.log('👉 Test 6: POST /api/maps/save');
|
||||
const testMapPath = 'img/location_photo/TDD_TEST_MAP.png';
|
||||
const testBoxes = [
|
||||
{
|
||||
x: '30.50',
|
||||
y: '40.25',
|
||||
w: '10.00',
|
||||
h: '12.00',
|
||||
asset_id: testAssetId
|
||||
},
|
||||
{
|
||||
x: '50.00',
|
||||
y: '60.00',
|
||||
w: '5.00',
|
||||
h: '5.00',
|
||||
asset_id: null
|
||||
}
|
||||
];
|
||||
|
||||
const res6 = await request('POST', '/api/maps/save', {
|
||||
path: testMapPath,
|
||||
boxes: testBoxes
|
||||
});
|
||||
assert.strictEqual(res6.status, 200, 'POST /api/maps/save should return 200');
|
||||
assert.strictEqual(res6.body.success, true, 'Save should be successful');
|
||||
console.log('✅ Test 6 Passed: Map coordinate save triggered successfully.');
|
||||
|
||||
// Verify DB update directly for physical_locations
|
||||
console.log('🔍 Verifying physical_locations update in database...');
|
||||
const [physLocCheck] = await connection.query(
|
||||
'SELECT location_code, map_x, map_y, map_w, map_h FROM physical_locations WHERE map_image = ? ORDER BY location_code',
|
||||
[testMapPath]
|
||||
);
|
||||
assert.strictEqual(physLocCheck.length, 2, 'Should create 2 physical locations for the test map');
|
||||
|
||||
// First location has asset_id mapped
|
||||
assert.strictEqual(parseFloat(physLocCheck[0].map_x).toFixed(2), '30.50', 'First location X coord match');
|
||||
assert.strictEqual(parseFloat(physLocCheck[0].map_y).toFixed(2), '40.25', 'First location Y coord match');
|
||||
assert.strictEqual(parseFloat(physLocCheck[0].map_w).toFixed(2), '10.00', 'First location W size match');
|
||||
assert.strictEqual(parseFloat(physLocCheck[0].map_h).toFixed(2), '12.00', 'First location H size match');
|
||||
|
||||
// Asset location coordinates sync check
|
||||
console.log('🔍 Verifying asset_location coordination sync in database...');
|
||||
const [assetLocSyncCheck] = await connection.query(
|
||||
'SELECT loc_x, loc_y, physical_location_code FROM asset_location WHERE asset_id = ? AND is_active = 1',
|
||||
[testAssetId]
|
||||
);
|
||||
assert(assetLocSyncCheck.length > 0, 'Asset location should be active');
|
||||
assert.strictEqual(parseFloat(assetLocSyncCheck[0].loc_x).toFixed(2), '30.50', 'Asset location X should sync');
|
||||
assert.strictEqual(parseFloat(assetLocSyncCheck[0].loc_y).toFixed(2), '40.25', 'Asset location Y should sync');
|
||||
assert.strictEqual(assetLocSyncCheck[0].physical_location_code, physLocCheck[0].location_code, 'Physical location code should match');
|
||||
console.log('✅ DB Verification for save: physical_locations and asset_location coordinates synced.');
|
||||
|
||||
// 7. Test GET /api/maps (After modification)
|
||||
console.log('👉 Test 7: GET /api/maps (After saving)');
|
||||
const res7 = await request('GET', '/api/maps');
|
||||
assert.strictEqual(res7.status, 200, 'GET /api/maps should return 200');
|
||||
assert(res7.body[testMapPath], 'Returned config should contain the newly saved test map');
|
||||
const savedBoxes = res7.body[testMapPath];
|
||||
assert.strictEqual(savedBoxes.length, 2, 'Saved boxes count match');
|
||||
assert.strictEqual(savedBoxes[0].asset_id, testAssetId, 'First box asset_id match');
|
||||
assert.strictEqual(savedBoxes[0].x, '30.50', 'First box X match');
|
||||
assert.strictEqual(savedBoxes[1].asset_id, null, 'Second box asset_id is null');
|
||||
console.log('✅ Test 7 Passed: GET /api/maps returned updated configuration.');
|
||||
|
||||
// Clean up
|
||||
console.log('🧹 Cleaning up test assets...');
|
||||
await connection.query("DELETE FROM asset_audit_pending WHERE asset_code = 'TEST-ASSET-001'");
|
||||
await connection.query("DELETE FROM asset_location WHERE asset_id = ?", [testAssetId]);
|
||||
await connection.query("DELETE FROM asset_core WHERE id = ?", [testAssetId]);
|
||||
await connection.query("DELETE FROM physical_locations WHERE map_image = ?", [testMapPath]);
|
||||
|
||||
console.log('🎉 All TDD tests passed successfully!');
|
||||
} catch (err) {
|
||||
console.error('❌ TDD Test Suite Failed:', err.message);
|
||||
throw err;
|
||||
} finally {
|
||||
connection.release();
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
runTests().catch(() => process.exit(1));
|
||||
Reference in New Issue
Block a user