diff --git a/application/src/main/java/run/halo/app/theme/router/SinglePageRoute.java b/application/src/main/java/run/halo/app/theme/router/SinglePageRoute.java index fdcd2e72d..068f11541 100644 --- a/application/src/main/java/run/halo/app/theme/router/SinglePageRoute.java +++ b/application/src/main/java/run/halo/app/theme/router/SinglePageRoute.java @@ -101,15 +101,21 @@ public class SinglePageRoute public Result reconcile(Request request) { client.fetch(SinglePage.class, request.name()) .ifPresent(page -> { - if (ExtensionOperator.isDeleted(page) - || BooleanUtils.isTrue(page.getSpec().getDeleted())) { - quickRouteMap.remove(NameSlugPair.from(page)); + var nameSlugPair = NameSlugPair.from(page); + if (ExtensionOperator.isDeleted(page)) { + quickRouteMap.remove(nameSlugPair); return; } - // put new one - quickRouteMap.entrySet() - .removeIf(entry -> entry.getKey().name().equals(request.name())); - quickRouteMap.put(NameSlugPair.from(page), handlerFunction(request.name())); + if (BooleanUtils.isTrue(page.getSpec().getDeleted())) { + quickRouteMap.remove(nameSlugPair); + } else { + // put new one + if (page.isPublished()) { + quickRouteMap.put(nameSlugPair, handlerFunction(request.name())); + } else { + quickRouteMap.remove(nameSlugPair); + } + } }); return new Result(false, null); } diff --git a/application/src/test/java/run/halo/app/theme/router/SinglePageRouteTest.java b/application/src/test/java/run/halo/app/theme/router/SinglePageRouteTest.java index d12ab8f54..657482b3d 100644 --- a/application/src/test/java/run/halo/app/theme/router/SinglePageRouteTest.java +++ b/application/src/test/java/run/halo/app/theme/router/SinglePageRouteTest.java @@ -2,16 +2,24 @@ package run.halo.app.theme.router; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.net.URI; +import java.time.Instant; +import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -31,8 +39,10 @@ import org.springframework.web.util.UriUtils; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import run.halo.app.core.extension.content.SinglePage; +import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.GroupVersionKind; import run.halo.app.extension.Metadata; +import run.halo.app.extension.controller.Reconciler; import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.finders.SinglePageFinder; import run.halo.app.theme.finders.vo.SinglePageVo; @@ -48,16 +58,19 @@ import run.halo.app.theme.router.SinglePageRoute.NameSlugPair; class SinglePageRouteTest { @Mock - private ViewNameResolver viewNameResolver; + ViewNameResolver viewNameResolver; @Mock - private SinglePageFinder singlePageFinder; + SinglePageFinder singlePageFinder; @Mock - protected ViewResolver viewResolver; + ViewResolver viewResolver; + + @Mock + ExtensionClient client; @InjectMocks - private SinglePageRoute singlePageRoute; + SinglePageRoute singlePageRoute; @Test void handlerFunction() { @@ -130,4 +143,102 @@ class SinglePageRouteTest { .verifyComplete(); } + @Nested + class SinglePageReconcilerTest { + + @Test + void shouldRemoveRouteIfSinglePageUnpublished() { + var name = "fake-single-page"; + var page = newSinglePage(name, false); + when(client.fetch(SinglePage.class, name)).thenReturn( + Optional.of(page)); + + var routeMap = Mockito.>>mock( + invocation -> new HashMap>()); + singlePageRoute.setQuickRouteMap(routeMap); + var result = singlePageRoute.reconcile(new Reconciler.Request(name)); + assertNotNull(result); + assertFalse(result.reEnqueue()); + verify(client).fetch(SinglePage.class, name); + verify(routeMap).remove(NameSlugPair.from(page)); + } + + @Test + void shouldAddRouteIfSinglePagePublished() { + var name = "fake-single-page"; + var page = newSinglePage(name, true); + when(client.fetch(SinglePage.class, name)).thenReturn( + Optional.of(page)); + + var routeMap = Mockito.>>mock( + invocation -> new HashMap>()); + singlePageRoute.setQuickRouteMap(routeMap); + var result = singlePageRoute.reconcile(new Reconciler.Request(name)); + assertNotNull(result); + assertFalse(result.reEnqueue()); + verify(client).fetch(SinglePage.class, name); + verify(routeMap).put(eq(NameSlugPair.from(page)), any()); + } + + @Test + void shouldRemoveRouteIfSinglePageDeleted() { + var name = "fake-single-page"; + var page = newDeletedSinglePage(name); + when(client.fetch(SinglePage.class, name)).thenReturn( + Optional.of(page)); + + var routeMap = Mockito.>>mock( + invocation -> new HashMap>()); + singlePageRoute.setQuickRouteMap(routeMap); + var result = singlePageRoute.reconcile(new Reconciler.Request(name)); + assertNotNull(result); + assertFalse(result.reEnqueue()); + verify(client).fetch(SinglePage.class, name); + verify(routeMap).remove(NameSlugPair.from(page)); + } + + @Test + void shouldRemoveRouteIfSinglePageRecycled() { + var name = "fake-single-page"; + var page = newRecycledSinglePage(name); + when(client.fetch(SinglePage.class, name)).thenReturn( + Optional.of(page)); + + var routeMap = Mockito.>>mock( + invocation -> new HashMap>()); + singlePageRoute.setQuickRouteMap(routeMap); + var result = singlePageRoute.reconcile(new Reconciler.Request(name)); + assertNotNull(result); + assertFalse(result.reEnqueue()); + verify(client).fetch(SinglePage.class, name); + verify(routeMap).remove(NameSlugPair.from(page)); + } + + + SinglePage newSinglePage(String name, boolean published) { + var metadata = new Metadata(); + metadata.setName(name); + var page = new SinglePage(); + page.setMetadata(metadata); + var spec = new SinglePage.SinglePageSpec(); + spec.setSlug("/fake-slug"); + page.setSpec(spec); + var status = new SinglePage.SinglePageStatus(); + page.setStatus(status); + SinglePage.changePublishedState(page, published); + return page; + } + + SinglePage newDeletedSinglePage(String name) { + var page = newSinglePage(name, true); + page.getMetadata().setDeletionTimestamp(Instant.now()); + return page; + } + + SinglePage newRecycledSinglePage(String name) { + var page = newSinglePage(name, true); + page.getSpec().setDeleted(true); + return page; + } + } }