package org.jooby.internal; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.junit.Assert.assertEquals; import java.util.Optional; import java.util.concurrent.TimeUnit; import org.jooby.Cookie; import org.jooby.Mutant; import org.jooby.Request; import org.jooby.Response; import org.jooby.Session; import org.jooby.Session.Definition; import org.jooby.Session.Store; import org.jooby.internal.parser.ParserExecutor; import org.jooby.test.MockUnit; import org.jooby.test.MockUnit.Block; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.typesafe.config.Config; @RunWith(PowerMockRunner.class) @PrepareForTest({ServerSessionManager.class, SessionImpl.class, Cookie.class }) public class ServerSessionManagerTest { private Block noSecret = unit -> { Config config = unit.get(Config.class); expect(config.hasPath("application.secret")).andReturn(false); }; private Block cookie = unit -> { Definition session = unit.get(Session.Definition.class); expect(session.cookie()).andReturn(unit.get(Cookie.Definition.class)); }; private Block storeGet = unit -> { Store store = unit.get(Store.class); expect(store.get(unit.get(Session.Builder.class))) .andReturn(unit.get(SessionImpl.class)); }; @Test public void newServerSessionManager() throws Exception { new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .run(unit -> { new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)); }); } @Test public void destroy() throws Exception { new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, Session.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(unit -> { Session session = unit.get(Session.class); expect(session.id()).andReturn("sid"); Store store = unit.get(Session.Store.class); store.delete("sid"); }) .run(unit -> { new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .destroy(unit.get(Session.class)); }); } @Test public void storeCreateSession() throws Exception { new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, RequestScopedSession.class, SessionImpl.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(reqSession()) .expect(unit -> { SessionImpl session = unit.get(SessionImpl.class); session.touch(); expect(session.isNew()).andReturn(true); session.aboutToSave(); Store store = unit.get(Store.class); store.create(session); session.markAsSaved(); }) .run(unit -> { new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .requestDone(unit.get(RequestScopedSession.class)); }); } @Test public void storeDirtySession() throws Exception { new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, RequestScopedSession.class, SessionImpl.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(reqSession()) .expect(unit -> { SessionImpl session = unit.get(SessionImpl.class); session.touch(); expect(session.isNew()).andReturn(false); expect(session.isDirty()).andReturn(true); session.aboutToSave(); Store store = unit.get(Store.class); store.save(session); session.markAsSaved(); }) .run(unit -> { new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .requestDone(unit.get(RequestScopedSession.class)); }); } @Test public void storeSaveIntervalSession() throws Exception { new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, RequestScopedSession.class, SessionImpl.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(reqSession()) .expect(unit -> { SessionImpl session = unit.get(SessionImpl.class); session.touch(); expect(session.isNew()).andReturn(false); expect(session.isDirty()).andReturn(false); expect(session.savedAt()).andReturn(0L); session.aboutToSave(); Store store = unit.get(Store.class); store.save(session); session.markAsSaved(); }) .run(unit -> { new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .requestDone(unit.get(RequestScopedSession.class)); }); } @Test public void storeSkipSaveIntervalSession() throws Exception { new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, RequestScopedSession.class, SessionImpl.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(reqSession()) .expect(unit -> { SessionImpl session = unit.get(SessionImpl.class); session.touch(); expect(session.isNew()).andReturn(false); expect(session.isDirty()).andReturn(false); expect(session.savedAt()).andReturn(Long.MAX_VALUE); session.markAsSaved(); }) .run(unit -> { new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .requestDone(unit.get(RequestScopedSession.class)); }); } @Test public void storeFailure() throws Exception { new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, RequestScopedSession.class, SessionImpl.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(reqSession()) .expect(unit -> { SessionImpl session = unit.get(SessionImpl.class); session.touch(); expect(session.isNew()).andReturn(true); session.aboutToSave(); Store store = unit.get(Store.class); store.create(session); expectLastCall().andThrow(new IllegalStateException("intentional err")); }) .run(unit -> { new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .requestDone(unit.get(RequestScopedSession.class)); }); } private Block reqSession() { return unit -> { RequestScopedSession req = unit.get(RequestScopedSession.class); expect(req.session()).andReturn(unit.get(SessionImpl.class)); }; } @Test public void noSession() throws Exception { new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, Request.class, Response.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(unit -> { Cookie.Definition cookie = unit.get(Cookie.Definition.class); expect(cookie.name()).andReturn(Optional.of("sid")); Mutant mutant = unit.mock(Mutant.class); expect(mutant.toOptional()).andReturn(Optional.empty()); Request req = unit.get(Request.class); expect(req.cookie("sid")).andReturn(mutant); }) .run(unit -> { Session session = new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .get(unit.get(Request.class), unit.get(Response.class)); assertEquals(null, session); }); } @Test public void getSession() throws Exception { String id = "xyz"; new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, Request.class, Response.class, SessionImpl.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(unit -> { Cookie.Definition cookie = unit.get(Cookie.Definition.class); expect(cookie.name()).andReturn(Optional.of("sid")); Mutant mutant = unit.mock(Mutant.class); expect(mutant.toOptional()).andReturn(Optional.of(id)); Request req = unit.get(Request.class); expect(req.cookie("sid")).andReturn(mutant); }) .expect(sessionBuilder(id, false, -1)) .expect(storeGet) .run(unit -> { Session session = new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .get(unit.get(Request.class), unit.get(Response.class)); assertEquals(unit.get(SessionImpl.class), session); }); } @Test public void getTouchSessionCookie() throws Exception { String id = "xyz"; new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, Request.class, Response.class, SessionImpl.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(30)) .expect(unit -> { Cookie.Definition cookie = unit.get(Cookie.Definition.class); expect(cookie.name()).andReturn(Optional.of("sid")); Mutant mutant = unit.mock(Mutant.class); expect(mutant.toOptional()).andReturn(Optional.of(id)); Request req = unit.get(Request.class); expect(req.cookie("sid")).andReturn(mutant); }) .expect(sessionBuilder(id, false, TimeUnit.SECONDS.toMillis(30))) .expect(storeGet) .expect(unsignedCookie(id)) .expect(session(id)) .expect(sendCookie()) .run(unit -> { Session session = new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .get(unit.get(Request.class), unit.get(Response.class)); assertEquals(unit.get(SessionImpl.class), session); }); } @Test public void getSignedSession() throws Exception { String id = "xyz"; new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, Request.class, Response.class, SessionImpl.class) .expect(secret("querty")) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(unit -> { Cookie.Definition cookie = unit.get(Cookie.Definition.class); expect(cookie.name()).andReturn(Optional.of("sid")); Mutant mutant = unit.mock(Mutant.class); expect(mutant.toOptional()).andReturn(Optional.of(id)); Request req = unit.get(Request.class); expect(req.cookie("sid")).andReturn(mutant); }) .expect(unit -> { unit.mockStatic(Cookie.Signature.class); expect(Cookie.Signature.unsign(id, "querty")).andReturn("unsigned"); }) .expect(sessionBuilder("unsigned", false, -1)) .expect(storeGet) .run(unit -> { Session session = new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .get(unit.get(Request.class), unit.get(Response.class)); assertEquals(unit.get(SessionImpl.class), session); }); } @Test public void createSession() throws Exception { new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, Request.class, Response.class, SessionImpl.class) .expect(noSecret) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(genID("123")) .expect(sessionBuilder("123", true, -1)) .expect(session("123")) .expect(unsignedCookie("123")) .expect(sendCookie()) .run(unit -> { new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .create(unit.get(Request.class), unit.get(Response.class)); }); } @Test public void createSignedCookieSession() throws Exception { new MockUnit(Config.class, Session.Definition.class, Cookie.Definition.class, Session.Store.class, ParserExecutor.class, Request.class, Response.class, SessionImpl.class) .expect(secret("querty")) .expect(cookie) .expect(saveInterval(-1L)) .expect(maxAge(-1)) .expect(genID("123")) .expect(sessionBuilder("123", true, -1)) .expect(session("123")) .expect(signCookie("querty", "123", "signed")) .expect(sendCookie()) .run(unit -> { new ServerSessionManager(unit.get(Config.class), unit.get(Session.Definition.class), unit.get(Session.Store.class), unit.get(ParserExecutor.class)) .create(unit.get(Request.class), unit.get(Response.class)); }); } private Block signCookie(final String secret, final String value, final String signed) { return unit -> { unit.mockStatic(Cookie.Signature.class); expect(Cookie.Signature.sign(value, secret)).andReturn(signed); Cookie.Definition cookie = unit.get(Cookie.Definition.class); Cookie.Definition newCookie = unit.constructor(Cookie.Definition.class) .build(cookie); expect(newCookie.value(signed)).andReturn(newCookie); unit.registerMock(Cookie.Definition.class, newCookie); }; } private Block secret(final String secret) { return unit -> { Config config = unit.get(Config.class); expect(config.hasPath("application.secret")).andReturn(true); expect(config.getString("application.secret")).andReturn(secret); }; } private Block unsignedCookie(final String id) { return unit -> { Cookie.Definition cookie = unit.get(Cookie.Definition.class); Cookie.Definition newCookie = unit.constructor(Cookie.Definition.class) .build(cookie); expect(newCookie.value(id)).andReturn(newCookie); unit.registerMock(Cookie.Definition.class, newCookie); }; } private Block sendCookie() { return unit -> { Cookie.Definition cookie = unit.get(Cookie.Definition.class); Response rsp = unit.get(Response.class); expect(rsp.cookie(cookie)).andReturn(rsp); }; } private Block session(final String sid) { return unit -> { SessionImpl session = unit.get(SessionImpl.class); expect(session.id()).andReturn(sid); }; } private Block sessionBuilder(final String id, final boolean isNew, final long timeout) { return unit -> { SessionImpl.Builder builder = unit.constructor(SessionImpl.Builder.class) .build(unit.get(ParserExecutor.class), isNew, id, timeout); if (isNew) { expect(builder.build()).andReturn(unit.get(SessionImpl.class)); } unit.registerMock(Session.Builder.class, builder); }; } private Block genID(final String id) { return unit -> { Store store = unit.get(Session.Store.class); expect(store.generateID()).andReturn(id); }; } private Block saveInterval(final Long saveInterval) { return unit -> { Definition session = unit.get(Session.Definition.class); expect(session.saveInterval()).andReturn(Optional.of(saveInterval)); }; } private Block maxAge(final Integer maxAge) { return unit -> { Cookie.Definition session = unit.get(Cookie.Definition.class); expect(session.maxAge()).andReturn(Optional.of(maxAge)); }; } }