Search code examples
swiftdockerunit-testingredisvapor

How to reset a Redis database set in Docker after a test?


I have the user access token saved in the Redis database on a Docker container when running tests.

The first time the test runs, it works well. But because I can not manage to reset the Docker container, the access token leaves on from other tests, which makes the test fails as the user session still exists in between tests.

The access token is used with the AsyncBearerAuthenticator, which ensure that the passed token in the headers validates the user on a desired route.

I have created this helper, to save the token in the Redis database when testing. But I would like to delete the token after each test, or completely reset the database.

extension Application {

  @discardableResult
  func test(
    _ method: HTTPMethod,
    _ path: String,
    user: UserModel? = .none,
    tokenProtected: Bool = true,
    content: (any Content)? = .none,
    afterResponse: (XCTHTTPResponse) throws -> () = { _ in },
    file: StaticString = #file,
    line: UInt = #line
  ) async throws -> XCTApplicationTester {

    var headers: HTTPHeaders = [:]
    if let user, tokenProtected {
      let accessToken = try AccessTokenModel.generate(for: user)
      try await self.redis.set(RedisKey(accessToken.value), toJSON: accessToken)
      headers.add(name: "Authorization", value: "Bearer \(accessToken.value)")
    }

    return try test(
      method,
      path,
      headers: headers,
      file: file,
      line: line,
      beforeRequest: { if let content { try $0.content.encode(content) }},
      afterResponse: afterResponse
    )
  }
}

This is the way I have configure Redis for the tests in the configure.swift file.

app.redis.configuration = try RedisConfiguration(hostname: "localhost", port: 6380)
app.sessions.use(.redis)

This is the way I create the Docker container for Redis.

docker run --name myapp_redis_testing -p 6380:6379 -d redis

Solution

  • To clear the Redis database set on Docker after each test, the command FLUSHALL must be triggered within the tear down of the test suit.

    So in the method func tearDownWithError() throws add the redis command to clear the database.

    var sut: Application!
    
    override func setUpWithError() throws {
      try super.setUpWithError()
      sut = Application(.testing)
      try configure(sut)
      try sut.boot()
    }
    
    override func tearDownWithError() throws {
      _ = try sut.redis.send(command: "FLUSHALL").wait() // Reset Redis container
      sut.shutdown()
      sut = .none
      try super.tearDownWithError()
    }