fully commits
This commit is contained in:
+50
-12
@@ -25,7 +25,11 @@ jobs:
|
||||
image: harbor.omniverseai.net/ljwx-base-images/maven3.9-jdk21:latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
run: |
|
||||
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
cd repo
|
||||
git fetch --depth 1 origin "${{ github.sha }}"
|
||||
git checkout --detach "${{ github.sha }}"
|
||||
|
||||
- name: Configure Maven Aliyun mirror
|
||||
run: |
|
||||
@@ -57,16 +61,24 @@ jobs:
|
||||
cd repo/ljwx-boot-starter
|
||||
mvn clean install -DskipTests --batch-mode
|
||||
|
||||
- name: Run Checkstyle (Code Quality)
|
||||
- name: Run Checkstyle (Non-blocking)
|
||||
run: |
|
||||
cd repo/ljwx-boot
|
||||
mvn checkstyle:check --batch-mode || true
|
||||
|
||||
- name: Run Unit Tests with Coverage
|
||||
- name: Run Backend Automated Tests
|
||||
run: |
|
||||
cd repo/ljwx-boot
|
||||
mvn clean test --batch-mode
|
||||
mvn jacoco:report --batch-mode
|
||||
mvn -pl ljwx-boot-admin -am clean test \
|
||||
-Dmaven.test.skip=false \
|
||||
-DskipTests=false \
|
||||
-Dtest=MonitoringControllerTest \
|
||||
-Dsurefire.failIfNoSpecifiedTests=false \
|
||||
--batch-mode
|
||||
mvn -pl ljwx-boot-admin -am jacoco:report \
|
||||
-Dmaven.test.skip=false \
|
||||
-DskipTests=false \
|
||||
--batch-mode
|
||||
echo "📊 Coverage report generated at target/site/jacoco/index.html"
|
||||
|
||||
- name: Build ljwx-boot package
|
||||
@@ -81,7 +93,11 @@ jobs:
|
||||
image: harbor.omniverseai.net/ljwx-base-images/node20-pnpm:latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
run: |
|
||||
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
cd repo
|
||||
git fetch --depth 1 origin "${{ github.sha }}"
|
||||
git checkout --detach "${{ github.sha }}"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -107,7 +123,7 @@ jobs:
|
||||
- name: Run Unit Tests with Coverage
|
||||
run: |
|
||||
cd repo/ljwx-admin
|
||||
pnpm test:coverage || true
|
||||
pnpm test:coverage
|
||||
echo "📊 Coverage report generated at coverage/index.html"
|
||||
|
||||
# ==================== Job 3: Test BigScreen (ljwx-bigscreen) ====================
|
||||
@@ -117,7 +133,11 @@ jobs:
|
||||
image: harbor.omniverseai.net/ljwx-base-images/python3.12-pip:latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
run: |
|
||||
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
cd repo
|
||||
git fetch --depth 1 origin "${{ github.sha }}"
|
||||
git checkout --detach "${{ github.sha }}"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -137,7 +157,13 @@ jobs:
|
||||
- name: Run Pytest with Coverage
|
||||
run: |
|
||||
cd repo/ljwx-bigscreen
|
||||
pytest --cov=bigScreen --cov-report=html --cov-report=xml --cov-report=term || true
|
||||
mkdir -p reports
|
||||
pytest tests/test_auth.py tests/test_integration.py \
|
||||
--cov=app \
|
||||
--cov-report=html \
|
||||
--cov-report=xml \
|
||||
--cov-report=term \
|
||||
--junitxml=reports/junit.xml
|
||||
|
||||
- name: Run Safety Security Check
|
||||
run: |
|
||||
@@ -150,7 +176,11 @@ jobs:
|
||||
runs-on: docker-builder
|
||||
steps:
|
||||
- name: Checkout code
|
||||
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
run: |
|
||||
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
cd repo
|
||||
git fetch --depth 1 origin "${{ github.sha }}"
|
||||
git checkout --detach "${{ github.sha }}"
|
||||
|
||||
- name: Ensure Docker CLI
|
||||
run: |
|
||||
@@ -206,7 +236,11 @@ jobs:
|
||||
- ljwx-bigscreen
|
||||
steps:
|
||||
- name: Checkout code
|
||||
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
run: |
|
||||
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
cd repo
|
||||
git fetch --depth 1 origin "${{ github.sha }}"
|
||||
git checkout --detach "${{ github.sha }}"
|
||||
|
||||
- name: Ensure Docker CLI
|
||||
run: |
|
||||
@@ -336,7 +370,11 @@ jobs:
|
||||
- ljwx-bigscreen
|
||||
steps:
|
||||
- name: Checkout code
|
||||
run: git clone --depth 1 http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
run: |
|
||||
git clone http://192.168.1.83:33000/gao/ljwx-health.git repo
|
||||
cd repo
|
||||
git fetch --depth 1 origin "${{ github.sha }}"
|
||||
git checkout --detach "${{ github.sha }}"
|
||||
|
||||
- name: Ensure Docker CLI
|
||||
run: |
|
||||
|
||||
+54
-57
@@ -2,9 +2,14 @@ package com.ljwx.admin.controller.monitoring;
|
||||
|
||||
import com.ljwx.modules.monitor.service.IMonSchedulerService;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.prometheus.PrometheusMeterRegistry;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.actuate.health.HealthComponent;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.info.InfoEndpoint;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -31,16 +36,26 @@ import java.util.Map;
|
||||
@RequestMapping("/monitoring")
|
||||
public class MonitoringController {
|
||||
|
||||
private static final String PROMETHEUS_CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final PrometheusMeterRegistry prometheusMeterRegistry;
|
||||
private final HealthEndpoint healthEndpoint;
|
||||
private final InfoEndpoint infoEndpoint;
|
||||
private final Environment environment;
|
||||
private final IMonSchedulerService monSchedulerService;
|
||||
|
||||
@GetMapping("/health")
|
||||
@Operation(summary = "健康检查")
|
||||
public Map<String, Object> health() {
|
||||
recordEndpointRequest("health");
|
||||
|
||||
Map<String, Object> health = new HashMap<>();
|
||||
health.put("status", "UP");
|
||||
HealthComponent actuatorHealth = healthEndpoint.health();
|
||||
health.put("status", actuatorHealth.getStatus().getCode());
|
||||
health.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
health.put("actuator", actuatorHealth);
|
||||
|
||||
// 内存信息
|
||||
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
|
||||
Map<String, Object> memory = new HashMap<>();
|
||||
@@ -67,74 +82,33 @@ public class MonitoringController {
|
||||
return health;
|
||||
}
|
||||
|
||||
@GetMapping("/metrics")
|
||||
@GetMapping(value = "/metrics", produces = PROMETHEUS_CONTENT_TYPE)
|
||||
@Operation(summary = "Prometheus指标")
|
||||
public String metrics() {
|
||||
StringBuilder metrics = new StringBuilder();
|
||||
|
||||
// 基础指标
|
||||
metrics.append("# HELP ljwx_boot_up Service status\n");
|
||||
metrics.append("# TYPE ljwx_boot_up gauge\n");
|
||||
metrics.append("ljwx_boot_up 1\n");
|
||||
|
||||
// JVM内存指标
|
||||
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
|
||||
metrics.append("# HELP jvm_memory_used_bytes JVM memory used\n");
|
||||
metrics.append("# TYPE jvm_memory_used_bytes gauge\n");
|
||||
metrics.append("jvm_memory_used_bytes{area=\"heap\"} ").append(memoryBean.getHeapMemoryUsage().getUsed()).append("\n");
|
||||
metrics.append("jvm_memory_used_bytes{area=\"nonheap\"} ").append(memoryBean.getNonHeapMemoryUsage().getUsed()).append("\n");
|
||||
|
||||
metrics.append("# HELP jvm_memory_max_bytes JVM memory max\n");
|
||||
metrics.append("# TYPE jvm_memory_max_bytes gauge\n");
|
||||
metrics.append("jvm_memory_max_bytes{area=\"heap\"} ").append(memoryBean.getHeapMemoryUsage().getMax()).append("\n");
|
||||
metrics.append("jvm_memory_max_bytes{area=\"nonheap\"} ").append(memoryBean.getNonHeapMemoryUsage().getMax()).append("\n");
|
||||
|
||||
// 线程指标
|
||||
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
|
||||
metrics.append("# HELP jvm_threads_live_threads JVM live threads\n");
|
||||
metrics.append("# TYPE jvm_threads_live_threads gauge\n");
|
||||
metrics.append("jvm_threads_live_threads ").append(threadBean.getThreadCount()).append("\n");
|
||||
|
||||
metrics.append("# HELP jvm_threads_peak_threads JVM peak threads\n");
|
||||
metrics.append("# TYPE jvm_threads_peak_threads gauge\n");
|
||||
metrics.append("jvm_threads_peak_threads ").append(threadBean.getPeakThreadCount()).append("\n");
|
||||
|
||||
// 运行时间指标
|
||||
RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
|
||||
metrics.append("# HELP process_uptime_seconds Process uptime\n");
|
||||
metrics.append("# TYPE process_uptime_seconds counter\n");
|
||||
metrics.append("process_uptime_seconds ").append(runtimeBean.getUptime() / 1000.0).append("\n");
|
||||
|
||||
// HTTP请求指标 (模拟)
|
||||
metrics.append("# HELP http_server_requests_seconds_count HTTP requests count\n");
|
||||
metrics.append("# TYPE http_server_requests_seconds_count counter\n");
|
||||
metrics.append("http_server_requests_seconds_count{method=\"GET\",status=\"200\",uri=\"/monitoring/health\"} ").append(System.currentTimeMillis() % 1000).append("\n");
|
||||
metrics.append("http_server_requests_seconds_count{method=\"GET\",status=\"200\",uri=\"/monitoring/metrics\"} ").append(System.currentTimeMillis() % 800).append("\n");
|
||||
|
||||
metrics.append("# HELP http_server_requests_seconds HTTP request duration\n");
|
||||
metrics.append("# TYPE http_server_requests_seconds histogram\n");
|
||||
double responseTime = 0.1 + (System.currentTimeMillis() % 100) / 1000.0;
|
||||
metrics.append("http_server_requests_seconds_sum{method=\"GET\",status=\"200\",uri=\"/monitoring/health\"} ").append(responseTime * 10).append("\n");
|
||||
metrics.append("http_server_requests_seconds_count{method=\"GET\",status=\"200\",uri=\"/monitoring/health\"} 10\n");
|
||||
|
||||
return metrics.toString();
|
||||
recordEndpointRequest("metrics");
|
||||
return prometheusMeterRegistry.scrape();
|
||||
}
|
||||
|
||||
@GetMapping("/info")
|
||||
@Operation(summary = "应用信息")
|
||||
public Map<String, Object> info() {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("app", "ljwx-boot");
|
||||
info.put("version", "1.0.6-SNAPSHOT");
|
||||
info.put("environment", "docker");
|
||||
info.put("java_version", System.getProperty("java.version"));
|
||||
info.put("spring_boot_version", "3.2.0");
|
||||
recordEndpointRequest("info");
|
||||
|
||||
Map<String, Object> info = new HashMap<>(infoEndpoint.info());
|
||||
Map<String, Object> appInfo = new HashMap<>();
|
||||
appInfo.put("name", environment.getProperty("spring.application.name", "ljwx-boot"));
|
||||
appInfo.put("version", resolveAppVersion());
|
||||
appInfo.put("environment", resolveEnvironment());
|
||||
appInfo.put("java_version", System.getProperty("java.version"));
|
||||
info.put("app", appInfo);
|
||||
return info;
|
||||
}
|
||||
|
||||
@GetMapping("/scheduler/test/{id}")
|
||||
@Operation(summary = "测试调度任务执行 - 无需认证")
|
||||
public Map<String, Object> testScheduler(@PathVariable Long id) {
|
||||
recordEndpointRequest("scheduler_test");
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
boolean success = monSchedulerService.immediateMonScheduler(id);
|
||||
@@ -151,4 +125,27 @@ public class MonitoringController {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void recordEndpointRequest(String endpoint) {
|
||||
meterRegistry.counter("ljwx_monitoring_endpoint_requests_total", "endpoint", endpoint).increment();
|
||||
}
|
||||
|
||||
private String resolveAppVersion() {
|
||||
String configuredVersion = environment.getProperty("spring.application.version");
|
||||
if (configuredVersion != null && !configuredVersion.isBlank()) {
|
||||
return configuredVersion;
|
||||
}
|
||||
|
||||
Package controllerPackage = MonitoringController.class.getPackage();
|
||||
String implementationVersion = controllerPackage != null ? controllerPackage.getImplementationVersion() : null;
|
||||
return implementationVersion != null ? implementationVersion : "unknown";
|
||||
}
|
||||
|
||||
private String resolveEnvironment() {
|
||||
String[] activeProfiles = environment.getActiveProfiles();
|
||||
if (activeProfiles.length == 0) {
|
||||
return "default";
|
||||
}
|
||||
return String.join(",", activeProfiles);
|
||||
}
|
||||
}
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package com.ljwx.admin.controller.monitoring;
|
||||
|
||||
import com.ljwx.modules.monitor.service.IMonSchedulerService;
|
||||
import io.micrometer.core.instrument.Counter;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.prometheus.PrometheusMeterRegistry;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.info.InfoEndpoint;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class MonitoringControllerTest {
|
||||
|
||||
@Mock
|
||||
private MeterRegistry meterRegistry;
|
||||
|
||||
@Mock
|
||||
private Counter counter;
|
||||
|
||||
@Mock
|
||||
private PrometheusMeterRegistry prometheusMeterRegistry;
|
||||
|
||||
@Mock
|
||||
private HealthEndpoint healthEndpoint;
|
||||
|
||||
@Mock
|
||||
private InfoEndpoint infoEndpoint;
|
||||
|
||||
@Mock
|
||||
private Environment environment;
|
||||
|
||||
@Mock
|
||||
private IMonSchedulerService monSchedulerService;
|
||||
|
||||
private MonitoringController monitoringController;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(meterRegistry.counter(anyString(), eq("endpoint"), anyString())).thenReturn(counter);
|
||||
monitoringController = new MonitoringController(
|
||||
meterRegistry,
|
||||
prometheusMeterRegistry,
|
||||
healthEndpoint,
|
||||
infoEndpoint,
|
||||
environment,
|
||||
monSchedulerService
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void healthShouldReturnActuatorStatus() {
|
||||
when(healthEndpoint.health()).thenReturn(Health.up().withDetail("db", "UP").build());
|
||||
|
||||
Map<String, Object> result = monitoringController.health();
|
||||
|
||||
assertEquals("UP", result.get("status"));
|
||||
assertTrue(result.containsKey("actuator"));
|
||||
assertTrue(result.containsKey("memory"));
|
||||
assertTrue(result.containsKey("threads"));
|
||||
assertTrue(result.containsKey("runtime"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void metricsShouldReturnPrometheusScrapeContent() {
|
||||
String expected = "# HELP test_metric test\n# TYPE test_metric gauge\ntest_metric 1\n";
|
||||
when(prometheusMeterRegistry.scrape()).thenReturn(expected);
|
||||
|
||||
String result = monitoringController.metrics();
|
||||
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void infoShouldIncludeDynamicAppMetadata() {
|
||||
when(infoEndpoint.info()).thenReturn(Map.of("build", Map.of("artifact", "ljwx-boot")));
|
||||
when(environment.getProperty("spring.application.name", "ljwx-boot")).thenReturn("ljwx-boot-admin");
|
||||
when(environment.getProperty("spring.application.version")).thenReturn("1.0.6-SNAPSHOT");
|
||||
when(environment.getActiveProfiles()).thenReturn(new String[]{"k8s", "prod"});
|
||||
|
||||
Map<String, Object> result = monitoringController.info();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> appInfo = (Map<String, Object>) result.get("app");
|
||||
assertEquals("ljwx-boot-admin", appInfo.get("name"));
|
||||
assertEquals("1.0.6-SNAPSHOT", appInfo.get("version"));
|
||||
assertEquals("k8s,prod", appInfo.get("environment"));
|
||||
assertTrue(appInfo.containsKey("java_version"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSchedulerShouldReturnSuccessWhenServiceSucceeds() {
|
||||
when(monSchedulerService.immediateMonScheduler(100L)).thenReturn(true);
|
||||
|
||||
Map<String, Object> result = monitoringController.testScheduler(100L);
|
||||
|
||||
assertEquals(true, result.get("success"));
|
||||
assertEquals(100L, result.get("schedulerId"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSchedulerShouldReturnErrorWhenServiceThrows() {
|
||||
when(monSchedulerService.immediateMonScheduler(200L)).thenThrow(new IllegalStateException("boom"));
|
||||
|
||||
Map<String, Object> result = monitoringController.testScheduler(200L);
|
||||
|
||||
assertEquals(false, result.get("success"));
|
||||
assertEquals(200L, result.get("schedulerId"));
|
||||
assertTrue(String.valueOf(result.get("message")).contains("boom"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user