98 lines
3.4 KiB
Java
98 lines
3.4 KiB
Java
package com.nanri.aiimage.config;
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
/**
|
|
* 巡检只读 SQL 报表静态契约基座(task-194..199)。
|
|
* 校验各 inspection/*.sql:以 SELECT 开头、无任何写语句/锁语句、含 NOT EXISTS/JOIN 关联、
|
|
* 带 LIMIT、分号结尾、头注释含目的与只读声明。子类提供文件名。
|
|
*/
|
|
abstract class InspectionSqlFileTestBase {
|
|
|
|
abstract String fileName();
|
|
|
|
private Path path() {
|
|
return Path.of(System.getProperty("user.dir"), "src/main/resources/inspection", fileName());
|
|
}
|
|
|
|
private String sql() throws IOException {
|
|
return Files.readString(path());
|
|
}
|
|
|
|
private String statements() throws IOException {
|
|
// 去掉整行注释与空行后的可执行 SQL 文本(头注释说明可能含"清理/删除"等字样,须排除)
|
|
StringBuilder sb = new StringBuilder();
|
|
for (String line : sql().split("\n")) {
|
|
String t = line.trim();
|
|
if (t.isEmpty() || t.startsWith("--")) {
|
|
continue;
|
|
}
|
|
sb.append(line).append('\n');
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
@Test
|
|
void fileExists() throws IOException {
|
|
assertTrue(Files.exists(path()), "巡检 SQL 应存在: " + fileName());
|
|
}
|
|
|
|
@Test
|
|
void startsWithSelect() throws IOException {
|
|
String first = statements().stripLeading();
|
|
assertTrue(first.toUpperCase().startsWith("SELECT"), "可执行 SQL 应以 SELECT 开头: " + fileName());
|
|
}
|
|
|
|
private static final String[] FORBIDDEN_WRITE_TOKENS = {
|
|
"INSERT", "UPDATE", "DELETE", "DROP", "TRUNCATE", "ALTER", "CREATE",
|
|
"GRANT", "REPLACE", "CALL", "LOAD", "DO"
|
|
};
|
|
|
|
@Test
|
|
void readOnlyNoWriteKeywords() throws IOException {
|
|
String upper = statements().toUpperCase();
|
|
for (String kw : FORBIDDEN_WRITE_TOKENS) {
|
|
java.util.regex.Pattern p = java.util.regex.Pattern.compile("(?<!\\w)" + java.util.regex.Pattern.quote(kw) + "(?!\\w)");
|
|
assertFalse(p.matcher(upper).find(), fileName() + " 不应含写语句 " + kw);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
void hasRelationalJoinCondition() throws IOException {
|
|
String upper = statements().toUpperCase();
|
|
assertTrue(upper.contains("NOT EXISTS") || upper.contains("JOIN"),
|
|
fileName() + " 应含 NOT EXISTS 或 JOIN 关联条件");
|
|
}
|
|
|
|
@Test
|
|
void hasRowLimit() throws IOException {
|
|
assertTrue(statements().toUpperCase().contains("LIMIT"), fileName() + " 应带 LIMIT");
|
|
}
|
|
|
|
@Test
|
|
void noRowLock() throws IOException {
|
|
String upper = statements().toUpperCase();
|
|
assertFalse(upper.contains("FOR UPDATE"), fileName() + " 不应 FOR UPDATE 加锁");
|
|
assertFalse(upper.contains("LOCK"), fileName() + " 不应加锁");
|
|
}
|
|
|
|
@Test
|
|
void endsWithSemicolon() throws IOException {
|
|
assertTrue(sql().trim().endsWith(";"), fileName() + " 应以分号结尾");
|
|
}
|
|
|
|
@Test
|
|
void headerDocumentsPurposeAndReadOnly() throws IOException {
|
|
String text = sql();
|
|
assertTrue(text.contains("巡检"), fileName() + " 头注释应含巡检说明");
|
|
assertTrue(text.contains("只读"), fileName() + " 头注释应声明只读");
|
|
}
|
|
}
|