Skip to content

Commit

Permalink
Adding UnitTests coverage for filter/TokenVerificationFilter.lua
Browse files Browse the repository at this point in the history
Added GHA WF for running unit tests as part of CI pipeline

Improvements to Readme with instructions on how to run tests

Signed-off-by: Alfredo Gutierrez <[email protected]>
  • Loading branch information
AlfredoG87 committed Mar 22, 2024
1 parent 88a1836 commit dded2c5
Show file tree
Hide file tree
Showing 13 changed files with 703 additions and 13 deletions.
58 changes: 58 additions & 0 deletions .github/workflows/proxy-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Tests

on:
pull_request:
branches: [ main, release/**]
push:
branches: [ main, release/*]
tags: [ v* ]

env:
ACTIONS_RUNTIME_TOKEN: fake-token


jobs:
proxy-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Install Lua
uses: leafo/gh-actions-lua@v8
with:
luaVersion: '5.3'

- name: Install LuaRocks
uses: leafo/gh-actions-luarocks@v4

- name: Install lunatest
run: luarocks install lunatest

- name: Install luacov
run: luarocks install luacov

- name: Install luacov-console
run: luarocks install luacov-console

- name: Install cjson
run: luarocks install lua-cjson

- name: Install luasocket
run: luarocks install luasocket

- name: Print Current Directory
run: pwd && ls -la
working-directory: auth-layer-proxy/tests

- name: Run tests
run: lua test.lua
working-directory: auth-layer-proxy/tests

- name: Generate coverage report
run: luacov
working-directory: auth-layer-proxy/tests

- name: Generate Console Report
run: luacov-console ../filters/ && luacov-console ../filters/ -s && luacov-console ../filters/ -s > coverage.txt
working-directory: auth-layer-proxy/tests
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,7 @@ charts/hedera-the-graph-node/Chart.lock

# DS_Store
.DS_Store
auth-layer-proxy/tests/coverage.txt
auth-layer-proxy/tests/luacov.report.out
auth-layer-proxy/tests/luacov.report.out.index
auth-layer-proxy/tests/luacov.stats.out
33 changes: 33 additions & 0 deletions auth-layer-proxy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,36 @@ curl --location 'http://localhost:10000' \
}
}'
```


## Testing

The tests are written using lunatest and can be run using the following command:

Make sure to have installed the following prerequisites:
1. lua
2. luarocks

Install the following luarocks packages:

```bash
luarocks install lua-cjson
luarocks install luasocket
luarocks install lunatest
luarocks install luacov
luarocks install luacov-console
```

Open a terminal and navigate to the folder containing the `tests` folder and run the following command:

```bash
lua test.lua
```

to show the coverage report, run the following command:

```bash
luacov
luacov-console ../filters/
luacov-console ../filters/ -s
```
36 changes: 23 additions & 13 deletions auth-layer-proxy/filters/TokenVerificationFilter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.

local TokenVerificationFilter = {}

local cjson = require("cjson")
local http = require("socket.http")
Expand Down Expand Up @@ -96,7 +97,7 @@ local tokenUser = "";
local function checkTokenPermissions(token, subgraphName, method)

-- Prepare the HTTP request body
local requestBody = "token=" .. token .. "&client_id=" .. clientId .. "&client_secret=" .. clientSecret
local requestBody = "token=" .. token .. "&client_id=" .. clientId .. "&client_secret=" .. clientSecret

-- Prepare the HTTP request headers
local headers = {
Expand All @@ -108,13 +109,13 @@ local function checkTokenPermissions(token, subgraphName, method)
local responseBody = {}

-- Perform the HTTP POST request
local response, statusCode, responseHeaders, statusText = http.request{
local response, statusCode, responseHeaders, statusText = http.request({
method = "POST",
url = introspectionUrl,
headers = headers,
source = ltn12.source.string(requestBody),
sink = ltn12.sink.table(responseBody)
}
})

-- Check if the request was successful
if statusCode == 200 then
Expand Down Expand Up @@ -162,15 +163,15 @@ end

-- This function is called for each request, and is the entry point for the filter
function envoy_on_request(request_handle)
local token = extractToken(request_handle)
local token = TokenVerificationFilter.extractToken(request_handle)

if not token then
request_handle:respond({[":status"] = "401"}, "No token provided")
return
end

local body = request_handle:body():getBytes(0, request_handle:body():length())
local method, subgraphName, parseError = parseJsonBody(body)
local method, subgraphName, parseError = TokenVerificationFilter.parseJsonBody(body)
if parseError then
request_handle:respond({[":status"] = "400"}, parseError)
return
Expand All @@ -181,24 +182,33 @@ function envoy_on_request(request_handle)
return
end

if not verifyValidMethod(method) then
if not TokenVerificationFilter.verifyValidMethod(method) then
request_handle:respond({[":status"] = "400"}, "Invalid method")
return
end

local hasPermission, permissionError = checkTokenPermissions(token, subgraphName, method)
local hasPermission, permissionError = TokenVerificationFilter.checkTokenPermissions(token, subgraphName, method)

if permissionError then
if not hasPermission or permissionError then
request_handle:logErr(permissionError)
request_handle:respond({[":status"] = "401"}, permissionError)
return
end

if not hasPermission then
request_handle:respond({[":status"] = "401"}, "Unauthorized")
return
end

print("Token is authorized for method: ".. method .. " and subgraph: " .. subgraphName.. " by the user".. tokenUser)
-- The request is authorized and processing continues
end

print("TokenVerificationFilter loaded")

-- Return the filter as a module for test purposes
TokenVerificationFilter.extractToken = extractToken
TokenVerificationFilter.parseJsonBody = parseJsonBody
TokenVerificationFilter.verifyValidMethod = verifyValidMethod
TokenVerificationFilter.checkTokenPermissions = checkTokenPermissions
TokenVerificationFilter.envoy_on_request = envoy_on_request

-- need to export the http module also, to be able to mock it.
TokenVerificationFilter.http = http

return TokenVerificationFilter
3 changes: 3 additions & 0 deletions auth-layer-proxy/tests/.luacov
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
modules = {
["TokenVerificationFilter"] = "../filters/TokenVerificationFilter.lua",
}
17 changes: 17 additions & 0 deletions auth-layer-proxy/tests/test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pcall(require, "luacov") --measure code coverage, if luacov is present
local lunatest = require "lunatest"


print '=============================='
print('Tests of auth-layer-proxy')
print("Tests are being executed on current directory: " .. os.getenv("PWD"))
print '=============================='

lunatest.suite("test_checkTokenPermissions")
lunatest.suite("test_extractToken")
lunatest.suite("test_parseJsonBody")
lunatest.suite("test_verifyValidMethod")
lunatest.suite("test_envoy_on_request")


lunatest.run()
76 changes: 76 additions & 0 deletions auth-layer-proxy/tests/testMocks.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
local cjson = require("cjson")

-- Mock for HTTP request
local function mockHttpRequest(params)

local requestBody = params.source()
local token, clientId, clientSecret = requestBody:match("token=(.*)&client_id=(.*)&client_secret=(.*)")

-- You can adjust the response based on the input parameters for different test cases
if token == "validToken" then
statusCode = 200
response = cjson.encode({
active = true,
subgraph_access = "subgraph1,subgraph2",
email = "[email protected]",
email_verified = true,
["realm_access"] = { -- Use square brackets and quotes for the key
roles = { -- Use equals sign for assignment
"subgraph_create",
"subgraph_deploy",
"subgraph_resume",
"subgraph_pause"
}
}
})
params.sink(response)
return 1, statusCode, nil, nil
elseif token == "emailNotVerified" then
statusCode = 200
response = cjson.encode({
active = true,
subgraph_access = "subgraph1,subgraph2",
email = "[email protected]",
email_verified = false,
["realm_access"] = { -- Use square brackets and quotes for the key
roles = { -- Use equals sign for assignment
"subgraph_remove",
"subgraph_create",
"subgraph_deploy",
"subgraph_resume",
"subgraph_pause"
}
}
})
params.sink(response)
return 1, statusCode, nil, nil

elseif token == "invalidToken" then
statusCode = 200
response = cjson.encode({
active = false
})
params.sink(response)
return 1, statusCode, nil, nil
else
-- Simulate a failed HTTP request
statusCode = 500
statusText = "Internal Server Error"
return 1, statusCode, statusText, nil
end
end

local function mockGetEnv(envVarName)
local envVars = {
CLIENT_ID = "htg-auth_layer",
CLIENT_SECRET = "wEGsZafep01LKNPkJhiOMQSmgAGAMWUi",
TOKEN_INTROSPECTION_URL = "http://host.docker.internal:8080/realms/HederaTheGraph/protocol/openid-connect/token/introspect"
}
return envVars[envVarName]
end

-- export module
local testMocks = {}
testMocks.mockHttpRequest = mockHttpRequest
testMocks.mockGetEnv = mockGetEnv
return testMocks
22 changes: 22 additions & 0 deletions auth-layer-proxy/tests/testUtils.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
testUtils = {}

function printGreen(text)
print("\27[32m" .. text .. "\27[0m")
end

-- Function to print text in red
function printRed(text)
print("\27[31m" .. text .. "\27[0m")
end

function wrapTextInRed(text)
return "\27[31m" .. text .. "\27[0m"
end


-- export functions
testUtils.printGreen = printGreen
testUtils.printRed = printRed
testUtils.wrapTextInRed = wrapTextInRed

return testUtils
Loading

0 comments on commit dded2c5

Please sign in to comment.