Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ Given fetch field :path from previous JSON response
Given the response should contain the initial state :name with the following values:
Given the response should contain the initial state :name json that match with:
Given the following :appId app config is set
Given /^run the command "(?P<command>(?:[^"]|\\")*)"$/
Given the output of the last command should contain the following text:
Given the output of the last command should be empty
Given /^run the command "(?P<command>(?:[^"]|\\")*)" with result code (\d+)$/
Given /^run the bash command "(?P<command>(?:[^"]|\\")*)" with result code (\d+)$/
Given create an environment :name with value :value to be used by occ command
```

## Tips
Expand All @@ -83,6 +89,15 @@ Given sending "post" to ocs "/apps/libresign/api/v1/request-signature"
| file | {"base64":""} |
```

### Step: all steps that run commands

Before the command is executed, the following placeholders will be replaced:

| Placeholder | Value |
| -------------------- | ---------------------- |
| `<appRootDir>` | your app root dir |
| `<nextcloudRootDir>` | The Nextcloud root dir |

### Step: `fetch field :path from previous JSON response`

If the json response is an array, you can fetch specific values using this step. The fetched values is stored to be used by other steps.
Expand Down
9 changes: 9 additions & 0 deletions features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ private function getLastRequest(): RequestInfo {
return $lastRequest;
}

/**
* When whe run the test suit of this repository at GitHub Actions, is
* necessary to consider that we haven't Nextcloud installed and mock
* the real path of files.
*/
public static function findParentDirContainingFile(string $filename): string {
return __DIR__;
}

/**
* @inheritDoc
*/
Expand Down
17 changes: 17 additions & 0 deletions features/bootstrap/console.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/**
* When whe run the test suit of this repository at GitHub Actions, is
* necessary to consider that we haven't Nextcloud installed and mock
* the occ commands execution.
*/

if (in_array('invalid-command', $argv)) {
echo "Invalid command\n";
exit(1);
}

if (getenv('OC_PASS') === '123456') {
echo "I found the environment variable OC_PASS with value 123456\n";
exit;
}
31 changes: 31 additions & 0 deletions features/test.feature
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,34 @@ Feature: Test this extension
Then the response should contain the initial state "appid-json-array" json that match with:
| key | value |
| (jq).[0] | orange |

Scenario: Test list app directory with success
When run the bash command "ls <appRootDir>" with result code 0
Then the output of the last command should contain the following text:
"""
FeatureContext.php
"""

Scenario: Test list Nextcloud directory with success
When run the bash command "ls <nextcloudRootDir>" with result code 0

Scenario: Test run bash command with success
When run the bash command "true" with result code 0
Then the output of the last command should be empty

Scenario: Test run bash command with error
When run the bash command "false" with result code 1

Scenario: Run occ command with success
When run the command "status" with result code 0

Scenario: Run occ command with success
When run the command "invalid-command" with result code 1

Scenario: Create an environment with value to be used by occ command
When create an environment "OC_PASS" with value "123456" to be used by occ command
And run the command "fake-command" with result code 0
Then the output of the last command should contain the following text:
"""
I found the environment variable OC_PASS with value 123456
"""
109 changes: 103 additions & 6 deletions src/NextcloudApiContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ class NextcloudApiContext implements Context {
protected RunServerListener $server;
protected string $currentUser = '';
protected array $fields = [];
protected static array $environments = [];
protected static string $commandOutput = '';

/**
* @var string[]
*/
protected array $createdUsers = [];
protected static array $createdUsers = [];
protected ResponseInterface $response;
/** @var CookieJar[] */
protected $cookieJars;
Expand Down Expand Up @@ -66,8 +69,9 @@ public static function beforeSuite(BeforeSuiteScope $scope):void {
}

#[BeforeScenario()]
public function setUp(): void {
$this->createdUsers = [];
public function beforeScenario(): void {
self::$createdUsers = [];
self::$environments = [];
}

#[Given('as user :user')]
Expand Down Expand Up @@ -110,7 +114,7 @@ protected function createUser(string $user): void {
$this->sendOCSRequest('GET', '/cloud/users' . '/' . $user);
$this->assertStatusCode($this->response, 200, 'Failed to do first login');

$this->createdUsers[] = $user;
self::$createdUsers[] = $user;

$this->setCurrentUser($currentUser);
}
Expand Down Expand Up @@ -448,9 +452,102 @@ protected function parseText(string $text): string {
return $text;
}

#[Given('/^run the command "(?P<command>(?:[^"]|\\")*)"$/')]
public static function runCommand(string $command): array {
$console = static::findParentDirContainingFile('console.php');
$console .= '/console.php';
$fileOwnerUid = fileowner($console);
if (!is_int($fileOwnerUid)) {
throw new \Exception('The console file owner of ' . $console . ' is not an integer UID.');
}
$owner = posix_getpwuid($fileOwnerUid);
if ($owner === false) {
throw new \Exception('Could not retrieve owner information for UID ' . $fileOwnerUid);
}
$fullCommand = 'php ' . $console . ' ' . $command;
if (!empty(self::$environments)) {
$fullCommand = http_build_query(self::$environments, '', ' ') . ' ' . $fullCommand;
}
if (posix_getuid() !== $owner['uid']) {
$fullCommand = 'runuser -u ' . $owner['name'] . ' -- ' . $fullCommand;
}
$fullCommand .= ' 2>&1';
return self::runBashCommand($fullCommand);
}

public static function findParentDirContainingFile(string $filename): string {
$dir = getcwd();
if (is_bool($dir)) {
throw new \Exception('Could not get current working directory (getcwd() returned false)');
}

while ($dir !== dirname($dir)) {
if (file_exists($dir . DIRECTORY_SEPARATOR . $filename)) {
return $dir;
}
$dir = dirname($dir);
}

throw new \Exception('The file ' . $filename . ' was not found in the parent directories of ' . $dir);
}

private static function runBashCommand(string $command): array {
$command = str_replace('\"', '"', $command);
$patterns = [];
$replacements = [];
$fields = [
'appRootDir' => static::findParentDirContainingFile('appinfo'),
'nextcloudRootDir' => static::findParentDirContainingFile('console.php'),
];
foreach ($fields as $key => $value) {
$patterns[] = '/<' . $key . '>/';
$replacements[] = $value;
}
$command = preg_replace($patterns, $replacements, $command);
if (!is_string($command)) {
throw new \Exception('The command is not a string after preg_replace: ' . print_r($command, true));
}

exec($command, $output, $resultCode);
self::$commandOutput = implode("\n", $output);
return [
'command' => $command,
'output' => $output,
'resultCode' => $resultCode,
];
}

#[Given('the output of the last command should contain the following text:')]
public static function theOutputOfTheLastCommandContains(PyStringNode $text): void {
Assert::assertStringContainsString((string) $text, self::$commandOutput, 'The output of the last command does not contain: ' . $text);
}

#[Given('the output of the last command should be empty')]
public static function theOutputOfTheLastCommandShouldBeEmpty(): void {
Assert::assertEmpty(self::$commandOutput, 'The output of the last command should be empty, but got: ' . self::$commandOutput);
}

#[Given('/^run the command "(?P<command>(?:[^"]|\\")*)" with result code (\d+)$/')]
public static function runCommandWithResultCode(string $command, int $resultCode = 0): void {
$return = self::runCommand($command);
Assert::assertEquals($resultCode, $return['resultCode'], print_r($return, true));
}

#[Given('/^run the bash command "(?P<command>(?:[^"]|\\")*)" with result code (\d+)$/')]
public static function runBashCommandWithResultCode(string $command, int $resultCode = 0): void {
$return = self::runBashCommand($command);
Assert::assertEquals($resultCode, $return['resultCode'], print_r($return, true));
}

#[Given('create an environment :name with value :value to be used by occ command')]
public static function createAnEnvironmentWithValueToBeUsedByOccCommand(string $name, string $value):void {
self::$environments[$name] = $value;
}

#[AfterScenario()]
public function tearDown(): void {
foreach ($this->createdUsers as $user) {
self::$environments = [];
foreach (self::$createdUsers as $user) {
$this->deleteUser($user);
}
}
Expand All @@ -461,7 +558,7 @@ protected function deleteUser(string $user): ResponseInterface {
$this->sendOCSRequest('DELETE', '/cloud/users/' . $user);
$this->setCurrentUser($currentUser);

unset($this->createdUsers[array_search($user, $this->createdUsers, true)]);
unset(self::$createdUsers[array_search($user, self::$createdUsers, true)]);

return $this->response;
}
Expand Down
Loading