-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
1,300 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
package api | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/mock" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/getAlby/hub/api/mocks" | ||
"github.com/getAlby/hub/lnclient" | ||
"github.com/getAlby/hub/service" | ||
) | ||
|
||
func TestGetCustomNodeCommandDefinitions(t *testing.T) { | ||
lnClient := mocks.NewLNClient(t) | ||
svc := mocks.NewService(t) | ||
|
||
mockLNCommandDefs := []lnclient.CustomNodeCommandDef{ | ||
{ | ||
Name: "no_args", | ||
Description: "command without args", | ||
Args: nil, | ||
}, | ||
{ | ||
Name: "with_args", | ||
Description: "command with args", | ||
Args: []lnclient.CustomNodeCommandArgDef{ | ||
{Name: "arg1", Description: "first argument"}, | ||
{Name: "arg2", Description: "second argument"}, | ||
}, | ||
}, | ||
} | ||
|
||
expectedCommands := []CustomNodeCommandDef{ | ||
{ | ||
Name: "no_args", | ||
Description: "command without args", | ||
Args: []CustomNodeCommandArgDef{}, | ||
}, | ||
{ | ||
Name: "with_args", | ||
Description: "command with args", | ||
Args: []CustomNodeCommandArgDef{ | ||
{Name: "arg1", Description: "first argument"}, | ||
{Name: "arg2", Description: "second argument"}, | ||
}, | ||
}, | ||
} | ||
|
||
lnClient.On("GetCustomNodeCommandDefinitions").Return(mockLNCommandDefs) | ||
svc.On("GetLNClient").Return(lnClient) | ||
|
||
theAPI := instantiateAPIWithService(svc) | ||
|
||
commands, err := theAPI.GetCustomNodeCommands() | ||
require.NoError(t, err) | ||
require.NotNil(t, commands) | ||
require.ElementsMatch(t, expectedCommands, commands.Commands) | ||
} | ||
|
||
func TestExecuteCustomNodeCommand(t *testing.T) { | ||
type testCase struct { | ||
name string | ||
apiCommandLine string | ||
lnSupportedCommands []lnclient.CustomNodeCommandDef | ||
lnExpectedCommandReq *lnclient.CustomNodeCommandRequest | ||
lnResponse *lnclient.CustomNodeCommandResponse | ||
lnError error | ||
apiExpectedResponse interface{} | ||
apiExpectedErr string | ||
} | ||
|
||
// Successful execution of a command without args. | ||
testCaseOkNoArgs := testCase{ | ||
name: "command without args", | ||
apiCommandLine: "test_command", | ||
lnSupportedCommands: []lnclient.CustomNodeCommandDef{{Name: "test_command"}}, | ||
lnExpectedCommandReq: &lnclient.CustomNodeCommandRequest{Name: "test_command", Args: []lnclient.CustomNodeCommandArg{}}, | ||
lnResponse: &lnclient.CustomNodeCommandResponse{Response: "ok"}, | ||
lnError: nil, | ||
apiExpectedResponse: "ok", | ||
apiExpectedErr: "", | ||
} | ||
|
||
// Successful execution of a command with args. The command line contains | ||
// different arg value styles: with '=' and with space. | ||
testCaseOkWithArgs := testCase{ | ||
name: "command with args", | ||
apiCommandLine: "test_command --arg1=foo --arg2 bar", | ||
lnSupportedCommands: []lnclient.CustomNodeCommandDef{ | ||
{ | ||
Name: "test_command", | ||
Args: []lnclient.CustomNodeCommandArgDef{ | ||
{Name: "arg1", Description: "argument one"}, | ||
{Name: "arg2", Description: "argument two"}, | ||
}, | ||
}, | ||
}, | ||
lnExpectedCommandReq: &lnclient.CustomNodeCommandRequest{Name: "test_command", Args: []lnclient.CustomNodeCommandArg{ | ||
{Name: "arg1", Value: "foo"}, | ||
{Name: "arg2", Value: "bar"}, | ||
}}, | ||
lnResponse: &lnclient.CustomNodeCommandResponse{Response: "ok"}, | ||
lnError: nil, | ||
apiExpectedResponse: "ok", | ||
apiExpectedErr: "", | ||
} | ||
|
||
// Successful execution of a command with a possible but unset arg. | ||
testCaseOkWithUnsetArg := testCase{ | ||
name: "command with unset arg", | ||
apiCommandLine: "test_command", | ||
lnSupportedCommands: []lnclient.CustomNodeCommandDef{ | ||
{Name: "test_command", Args: []lnclient.CustomNodeCommandArgDef{{Name: "arg1", Description: "argument one"}}}, | ||
}, | ||
lnExpectedCommandReq: &lnclient.CustomNodeCommandRequest{Name: "test_command", Args: []lnclient.CustomNodeCommandArg{}}, | ||
lnResponse: &lnclient.CustomNodeCommandResponse{Response: "ok"}, | ||
lnError: nil, | ||
apiExpectedResponse: "ok", | ||
apiExpectedErr: "", | ||
} | ||
|
||
// Error: command line is empty. | ||
testCaseErrEmptyCommand := testCase{ | ||
name: "empty command", | ||
apiCommandLine: "", | ||
lnSupportedCommands: nil, | ||
lnExpectedCommandReq: nil, | ||
lnResponse: nil, | ||
lnError: nil, | ||
apiExpectedResponse: nil, | ||
apiExpectedErr: "no command provided", | ||
} | ||
|
||
// Error: command line is malformed, i.e. non-parseable. | ||
testCaseErrMalformedCommand := testCase{ | ||
name: "command with unclosed quote", | ||
apiCommandLine: "test_command\"", | ||
lnSupportedCommands: nil, | ||
lnExpectedCommandReq: nil, | ||
lnResponse: nil, | ||
lnError: nil, | ||
apiExpectedResponse: nil, | ||
apiExpectedErr: "failed to parse node command", | ||
} | ||
|
||
// Error: node does not support this command. | ||
testCaseErrUnknownCommand := testCase{ | ||
name: "unknown command", | ||
apiCommandLine: "test_command_unknown", | ||
lnSupportedCommands: []lnclient.CustomNodeCommandDef{{Name: "test_command"}}, | ||
lnExpectedCommandReq: nil, | ||
lnResponse: nil, | ||
lnError: nil, | ||
apiExpectedResponse: nil, | ||
apiExpectedErr: "unknown command", | ||
} | ||
|
||
// Error: the command is valid but the node fails to execute it. | ||
testCaseErrNodeFailed := testCase{ | ||
name: "node failed to execute command", | ||
apiCommandLine: "test_command", | ||
lnSupportedCommands: []lnclient.CustomNodeCommandDef{{Name: "test_command"}}, | ||
lnExpectedCommandReq: &lnclient.CustomNodeCommandRequest{Name: "test_command", Args: []lnclient.CustomNodeCommandArg{}}, | ||
lnResponse: nil, | ||
lnError: fmt.Errorf("utter failure"), | ||
apiExpectedResponse: nil, | ||
apiExpectedErr: "utter failure", | ||
} | ||
|
||
testCases := []testCase{ | ||
testCaseOkNoArgs, | ||
testCaseOkWithArgs, | ||
testCaseOkWithUnsetArg, | ||
testCaseErrEmptyCommand, | ||
testCaseErrMalformedCommand, | ||
testCaseErrUnknownCommand, | ||
testCaseErrNodeFailed, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
lnClient := mocks.NewLNClient(t) | ||
svc := mocks.NewService(t) | ||
|
||
if tc.lnSupportedCommands != nil { | ||
lnClient.On("GetCustomNodeCommandDefinitions").Return(tc.lnSupportedCommands) | ||
} | ||
|
||
if tc.lnExpectedCommandReq != nil { | ||
lnClient.On("ExecuteCustomNodeCommand", mock.Anything, tc.lnExpectedCommandReq).Return(tc.lnResponse, tc.lnError) | ||
} | ||
|
||
svc.On("GetLNClient").Return(lnClient) | ||
|
||
theAPI := instantiateAPIWithService(svc) | ||
|
||
response, err := theAPI.ExecuteCustomNodeCommand(context.TODO(), tc.apiCommandLine) | ||
require.Equal(t, tc.apiExpectedResponse, response) | ||
if tc.apiExpectedErr == "" { | ||
require.NoError(t, err) | ||
} else { | ||
require.ErrorContains(t, err, tc.apiExpectedErr) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
// instantiateAPIWithService is a helper function that returns a partially | ||
// constructed API instance. It is only suitable for the simplest of test cases. | ||
func instantiateAPIWithService(s service.Service) *api { | ||
return &api{svc: s} | ||
} |
Oops, something went wrong.