Repository: redhat-developer/service-binding-operator
Pull request #541 (2020-07-03 20:54:46)

Add acceptance tests - nodejs_postgresql
### Motivation

https://issues.redhat.com/browse/APPSVC-604

### Changes

Acceptance tests framework introduced
`nodejs_postgres` scenario implemented:

> As a user of Service Binding Operator
> I want to bind applications to services it depends on

### Testing

`make test-acceptance`

openshift-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **dhritishikhar**
You can assign the PR to them by writing `/assign @dhritishikhar` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=redhat-developer%2Fservice-binding-operator).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/redhat-developer/service-binding-operator/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["dhritishikhar"]} -->
openshift-ci-robot: @pmacik: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-e2e | 312a74872ca5e8f3cfa6813a971f87864e8c9e14 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1279156662803894272) | `/test 4.5-e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
pmacik: /retest
openshift-ci-robot: @pmacik: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-e2e | 312a74872ca5e8f3cfa6813a971f87864e8c9e14 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1279369478659903488) | `/test 4.5-e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
pmacik: /retest
openshift-ci-robot: @pmacik: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-e2e | 6445ef12f291b7a54266a1ce66bcdfc8089269db | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1280239560164380672) | `/test 4.5-e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @pmacik: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-e2e | 6445ef12f291b7a54266a1ce66bcdfc8089269db | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1280239560164380672) | `/test 4.5-e2e`
ci/prow/e2e | 140139aa1a3c4eda41042d7fbc0cde619e4fb257 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-e2e/1280430601773518848) | `/test e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
pmacik: /test e2e
openshift-ci-robot: @pmacik: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e | 0ac86da334d0278951b9694a4c0aceae5f537030 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-e2e/1280512277891518464) | `/test e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
pmacik: The `*e2e` jobs would likely fail until:
* https://github.com/redhat-developer/service-binding-operator/pull/544
* https://github.com/redhat-developer/service-binding-operator/pull/523
are merged
openshift-ci-robot: @pmacik: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e | 2a12f34bf981b04900976a896528b85343888ef5 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-e2e/1280534590598942720) | `/test e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @pmacik: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e | 2a12f34bf981b04900976a896528b85343888ef5 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-e2e/1280534590598942720) | `/test e2e`
ci/prow/4.5-e2e | 2a12f34bf981b04900976a896528b85343888ef5 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1280534589500035073) | `/test 4.5-e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
pmacik: /retest

openshift-ci-robot: @pmacik: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/e2e | 2a12f34bf981b04900976a896528b85343888ef5 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-e2e/1280804102791499776) | `/test e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
pmacik: /retest
openshift-ci-robot: @pmacik: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-e2e | 33b41ff277eddbee633fe931d756ae93bebb9b54 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1280822607796506624) | `/test 4.5-e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
pmacik: /test 4.5-e2e
/test e2e
openshift-ci-robot: @pmacik: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-e2e | 7090a030a3fa85700dfc18ae0e5457117bdac3a6 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1281211365914054656) | `/test 4.5-e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @pmacik: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-e2e | 7090a030a3fa85700dfc18ae0e5457117bdac3a6 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1281211365914054656) | `/test 4.5-e2e`
ci/prow/e2e | 7090a030a3fa85700dfc18ae0e5457117bdac3a6 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-e2e/1281211365993746432) | `/test e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @pmacik: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-e2e | 7090a030a3fa85700dfc18ae0e5457117bdac3a6 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1281211365914054656) | `/test 4.5-e2e`
ci/prow/e2e | 7090a030a3fa85700dfc18ae0e5457117bdac3a6 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-e2e/1281211365993746432) | `/test e2e`
ci/prow/lint | fdb497fa551517ac889e39b7014f52318ea0268d | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-lint/1281265746172710912) | `/test lint`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @pmacik: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-e2e | 7090a030a3fa85700dfc18ae0e5457117bdac3a6 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1281211365914054656) | `/test 4.5-e2e`
ci/prow/e2e | 7090a030a3fa85700dfc18ae0e5457117bdac3a6 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-e2e/1281211365993746432) | `/test e2e`
ci/prow/lint | fdb497fa551517ac889e39b7014f52318ea0268d | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-lint/1281265746172710912) | `/test lint`
ci/prow/4.5-lint | fdb497fa551517ac889e39b7014f52318ea0268d | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-lint/1281265746109796352) | `/test 4.5-lint`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @pmacik: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/lint | fdb497fa551517ac889e39b7014f52318ea0268d | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-lint/1281265746172710912) | `/test lint`
ci/prow/4.5-lint | fdb497fa551517ac889e39b7014f52318ea0268d | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-lint/1281265746109796352) | `/test 4.5-lint`
ci/prow/4.5-e2e | fdb497fa551517ac889e39b7014f52318ea0268d | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1281265746080436224) | `/test 4.5-e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @pmacik: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-e2e | ed03c072bf6178bd2049a8058d0634bfdb3633f4 | [link](https://prow.svc.ci.openshift.org/view/gcs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-e2e/1281359375285358592) | `/test 4.5-e2e`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
pmacik: /test all
openshift-ci-robot: @pmacik: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/lint | 864ae16b32ca212410e6c4467b3c95d38820d8e5 | [link](https://prow.svc.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-lint/1283348815394377728) | `/test lint`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @pmacik: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/lint | 864ae16b32ca212410e6c4467b3c95d38820d8e5 | [link](https://prow.svc.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-lint/1283348815394377728) | `/test lint`
ci/prow/4.5-lint | 864ae16b32ca212410e6c4467b3c95d38820d8e5 | [link](https://prow.svc.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-lint/1283348815448903680) | `/test 4.5-lint`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
pmacik: /test lint
/test 4.5-lint
openshift-ci-robot: @pmacik: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/4.5-lint | 864ae16b32ca212410e6c4467b3c95d38820d8e5 | [link](https://prow.svc.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-lint/1283348815448903680) | `/test 4.5-lint`
ci/prow/lint | 864ae16b32ca212410e6c4467b3c95d38820d8e5 | [link](https://prow.svc.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-lint/1283355146738208768) | `/test lint`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci-robot: @pmacik: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
ci/prow/lint | 864ae16b32ca212410e6c4467b3c95d38820d8e5 | [link](https://prow.svc.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-lint/1283355146738208768) | `/test lint`
ci/prow/4.5-lint | 864ae16b32ca212410e6c4467b3c95d38820d8e5 | [link](https://prow.svc.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/redhat-developer_service-binding-operator/541/pull-ci-redhat-developer-service-binding-operator-master-4.5-lint/1283355146713042944) | `/test 4.5-lint`

[Full PR test history](https://prow.svc.ci.openshift.org/pr-history?org=redhat-developer&repo=service-binding-operator&pr=541). [Your PR dashboard](https://prow.svc.ci.openshift.org/pr?query=is:pr+state:open+author:pmacik). Please help us cut down on flakes by [linking to](https://github.com/kubernetes/community/blob/master/contributors/devel/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/redhat-developer/service-binding-operator/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
pmacik: /retest

Patch:
diff --git a/.gitignore b/.gitignore
index 68b88de5..7bdf68ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -90,3 +90,5 @@ tags
 .idea
 ### Logs
 **/*.log
+venv
+**/*/__pycache__
diff --git a/Makefile b/Makefile
index 128b04a7..84be7a76 100644
--- a/Makefile
+++ b/Makefile
@@ -45,6 +45,8 @@ endif
 
 ZAP_FLAGS = $(ZAP_ENCODER_FLAG) $(ZAP_LEVEL_FLAG)
 
+SKIP_CLEANUP_ERROR ?= true
+
 # Create output directory for artifacts and test results. ./out is supposed to
 # be a safe place for all targets to write to while knowing that all content
 # inside of ./out is wiped once "make clean" is run.
@@ -154,11 +156,14 @@ BASE_COMMIT := $(shell echo $$CLONEREFS_OPTIONS | jq '.refs[0].base_sha')
 PR_COMMIT := $(shell echo $$CLONEREFS_OPTIONS | jq '.refs[0].pulls[0].sha')
 PULL_NUMBER := $(shell echo $$CLONEREFS_OPTIONS | jq '.refs[0].pulls[0].number')
 
+# -- Variables for acceptance tests
+TEST_ACCEPTANCE_START_SBO ?= local
+
 ## -- Static code analysis (lint) targets --
 
 .PHONY: lint
 ## Runs linters on Go code files and YAML files - DISABLED TEMPORARILY
-lint: setup-venv lint-go-code lint-yaml
+lint: setup-venv lint-go-code lint-yaml lint-python-code
 
 YAML_FILES := $(shell find . -path ./vendor -prune -o -type f -regex ".*y[a]ml" -print)
 .PHONY: lint-yaml
@@ -177,6 +182,11 @@ lint-go-code: $(GOLANGCI_LINT_BIN)
 $(GOLANGCI_LINT_BIN):
 	$(Q)curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./out v1.18.0
 
+## -- Check the python code
+.PHONY: lint-python-code
+lint-python-code:
+	$(Q)./hack/check-python/lint-python-code.sh
+
 .PHONY: setup-venv
 ## Setup virtual environment
 setup-venv:
@@ -219,7 +229,7 @@ test-e2e: e2e-setup deploy-crds
 		operator-sdk --verbose test local ./test/e2e \
 			--namespace $(TEST_NAMESPACE) \
 			--up-local \
-			--skip-cleanup-error=true \
+			--skip-cleanup-error=$(SKIP_CLEANUP_ERROR) \
 			--go-test-flags "-timeout=110m" \
 			--local-operator-flags "$(ZAP_FLAGS)" \
 			$(OPERATOR_SDK_EXTRA_ARGS) \
@@ -265,6 +275,31 @@ test-unit-with-coverage:
 		go test $(shell GOCACHE="$(GOCACHE)" go list ./...|grep -v e2e) $(GOCOV_FLAGS) -v -mod vendor $(TEST_EXTRA_ARGS)
 	$(Q)GOCACHE=$(GOCACHE) go tool cover -func=$(GOCOV_FILE)
 
+.PHONY: test-acceptance-setup
+## Setup the environment for the acceptance tests
+ifeq ($(TEST_ACCEPTANCE_START_SBO), local)
+test-acceptance-setup:
+	$(Q)echo "Starting local SBO instance"
+	$(eval TEST_ACCEPTANCE_SBO_STARTED := $(shell OPERATOR_NAMESPACE="$(TEST_NAMESPACE)" ZAP_FLAGS="$(ZAP_FLAGS)" ./hack/deploy-sbo-local.sh))
+else ifeq ($(TEST_ACCEPTANCE_START_SBO), operator-hub)
+test-acceptance-setup:
+	$(eval TEST_ACCEPTANCE_SBO_STARTED := $(shell ./hack/deploy-sbo-operator-hub.sh))
+endif
+
+.PHONY: set-test-namespace
+set-test-namespace: get-test-namespace
+	$(Q)oc project $(TEST_NAMESPACE)
+
+.PHONY: test-acceptance
+## Runs acceptance tests
+test-acceptance: e2e-setup set-test-namespace deploy-clean deploy-rbac deploy-crds test-acceptance-setup
+	$(Q)echo "Running acceptance tests"
+	$(Q)TEST_ACCEPTANCE_START_SBO=$(TEST_ACCEPTANCE_START_SBO) \
+		TEST_ACCEPTANCE_SBO_STARTED=$(TEST_ACCEPTANCE_SBO_STARTED) \
+		TEST_NAMESPACE=$(TEST_NAMESPACE) \
+		behave -v --no-capture --no-capture-stderr --tags="~@disabled" test/acceptance/features
+	$(Q)kill $(TEST_ACCEPTANCE_SBO_STARTED)
+
 .PHONY: test
 ## Test: Runs unit and integration (e2e) tests
 test: test-unit test-e2e
@@ -479,3 +514,4 @@ dev-release:
 validate-release: setup-venv
 	$(Q)$(OUTPUT_DIR)/venv3/bin/pip install yq==2.10.0
 	BUNDLE_VERSION=$(BASE_BUNDLE_VERSION) CHANNEL="alpha" ./hack/validate-release.sh
+
diff --git a/hack/check-python/check-PEP8-style.sh b/hack/check-python/check-PEP8-style.sh
new file mode 100755
index 00000000..359ceb9d
--- /dev/null
+++ b/hack/check-python/check-PEP8-style.sh
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+. ./hack/check-python/prepare-env.sh
+
+echo "----------------------------------------------------"
+echo "Running Python linter against following directories:"
+echo "$directories"
+echo "----------------------------------------------------"
+echo
+
+[ "$NOVENV" == "1" ] || prepare_venv || exit 1
+
+# checks for the whole directories
+for directory in $directories
+do
+    files=$(find "$directory" -path "$directory/venv" -prune -o -name '*.py' -print)
+
+    for source in $files
+    do
+        echo "$source"
+        pycodestyle "$source"
+        if [ $? -eq 0 ]
+        then
+            echo "    Pass"
+            let "pass++"
+        else
+            echo "    Fail"
+            let "fail++"
+        fi
+    done
+done
+
+
+if [ $fail -eq 0 ]
+then
+    echo "All checks passed for $pass source files"
+else
+    let total=$pass+$fail
+    echo "Linter fail, $fail source files out of $total source files need to be fixed"
+    exit 1
+fi
diff --git a/hack/check-python/check-python-docstyle.sh b/hack/check-python/check-python-docstyle.sh
new file mode 100755
index 00000000..28bb2e64
--- /dev/null
+++ b/hack/check-python/check-python-docstyle.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+
+. ./hack/check-python/prepare-env.sh
+
+# run the pydocstyle for all files that are provided in $1
+function check_files() {
+    for source in $1
+    do
+        echo "$source"
+        pydocstyle --count "$source"
+        if [ $? -eq 0 ]
+        then
+            echo "    Pass"
+            let "pass++"
+        elif [ $? -eq 2 ]
+        then
+            echo "    Illegal usage (should not happen)"
+            exit 2
+        else
+            echo "    Fail"
+            let "fail++"
+        fi
+    done
+}
+
+
+echo "----------------------------------------------------"
+echo "Checking documentation strings in all sources stored"
+echo "in following directories:"
+echo "$directories"
+echo "----------------------------------------------------"
+echo
+
+[ "$NOVENV" == "1" ] || prepare_venv || exit 1
+
+# checks for the whole directories
+for directory in $directories
+do
+    files=$(find "$directory" -path "$directory/venv" -prune -o -name '*.py' -print)
+
+    check_files "$files"
+done
+
+
+if [ $fail -eq 0 ]
+then
+    echo "All checks passed for $pass source files"
+else
+    let total=$pass+$fail
+    echo "Documentation strings should be added and/or fixed in $fail source files out of $total files"
+    exit 1
+fi
diff --git a/hack/check-python/detect-common-errors.sh b/hack/check-python/detect-common-errors.sh
new file mode 100755
index 00000000..13b0499e
--- /dev/null
+++ b/hack/check-python/detect-common-errors.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+. ./hack/check-python/prepare-env.sh
+
+# run the pyflakes for all files that are provided in $1
+function check_files() {
+    for source in $1
+    do
+        echo "$source"
+        pyflakes "$source"
+        if [ $? -eq 0 ]
+        then
+            echo "    Pass"
+            let "pass++"
+        elif [ $? -eq 2 ]
+        then
+            echo "    Illegal usage (should not happen)"
+            exit 2
+        else
+            echo "    Fail"
+            let "fail++"
+        fi
+    done
+}
+
+
+echo "----------------------------------------------------"
+echo "Checking source files for common errors in following"
+echo "directories:"
+echo "$directories"
+echo "----------------------------------------------------"
+echo
+
+[ "$NOVENV" == "1" ] || prepare_venv || exit 1
+
+# checks for the whole directories
+for directory in $directories
+do
+    files=$(find "$directory" -path "$directory/venv" -prune -o -name '*.py' -print)
+
+    check_files "$files"
+done
+
+
+if [ $fail -eq 0 ]
+then
+    echo "All checks passed for $pass source files"
+else
+    let total=$pass+$fail
+    echo "$fail source files out of $total files needs to be checked and fixed"
+    exit 1
+fi
diff --git a/hack/check-python/detect-dead-code.sh b/hack/check-python/detect-dead-code.sh
new file mode 100755
index 00000000..2c8c0e14
--- /dev/null
+++ b/hack/check-python/detect-dead-code.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+. ./hack/check-python/prepare-env.sh
+
+# run the vulture for all files that are provided in $1
+function check_files() {
+    for source in $1
+    do
+        echo "$source"
+        vulture --min-confidence 90 "$source"
+        if [ $? -eq 0 ]
+        then
+            echo "    Pass"
+            let "pass++"
+        elif [ $? -eq 2 ]
+        then
+            echo "    Illegal usage (should not happen)"
+            exit 2
+        else
+            echo "    Fail"
+            let "fail++"
+        fi
+    done
+}
+
+
+echo "----------------------------------------------------"
+echo "Checking source files for dead code and unused imports"
+echo "in following directories:"
+echo "$directories"
+echo "----------------------------------------------------"
+echo
+
+[ "$NOVENV" == "1" ] || prepare_venv || exit 1
+
+# checks for the whole directories
+for directory in $directories
+do
+    files=$(find "$directory" -path "$directory/venv" -prune -o -name '*.py' -print)
+
+    check_files "$files"
+done
+
+
+if [ $fail -eq 0 ]
+then
+    echo "All checks passed for $pass source files"
+else
+    let total=$pass+$fail
+    echo "$fail source files out of $total files seems to contain dead code and/or unused imports"
+    exit 1
+fi
diff --git a/hack/check-python/lint-python-code.sh b/hack/check-python/lint-python-code.sh
new file mode 100755
index 00000000..7fde34fd
--- /dev/null
+++ b/hack/check-python/lint-python-code.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+failed=""
+CHECK_PYTHON=./hack/check-python
+$CHECK_PYTHON/detect-common-errors.sh || failed="$failed\n - detect-common-errors"
+$CHECK_PYTHON/detect-dead-code.sh || failed="$failed\n - detect-dead-code"
+$CHECK_PYTHON/check-PEP8-style.sh || failed="$failed\n - check-PEP8-style"
+#$CHECK_PYTHON/check-python-docstyle.sh || failed="$failed\n - check-python-docstyle"
+$CHECK_PYTHON/measure-cyclomatic-complexity.sh || failed="$failed\n - measure-cyclomatic-complexity"
+$CHECK_PYTHON/measure-maintainability-index.sh || failed="$failed\n - measure-maintainability-index"
+
+if [ ! -z "$failed" ]; then
+    echo -e "\nERROR: Following python checks FAILED:$failed\n"
+    exit 1
+else
+    echo -e "\nAll python checks PASSED"
+fi
diff --git a/hack/check-python/measure-cyclomatic-complexity.sh b/hack/check-python/measure-cyclomatic-complexity.sh
new file mode 100755
index 00000000..4523bf5b
--- /dev/null
+++ b/hack/check-python/measure-cyclomatic-complexity.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+. ./hack/check-python/prepare-env.sh
+
+[ "$NOVENV" == "1" ] || prepare_venv || exit 1
+
+SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
+
+pushd "${SCRIPT_DIR}/.."
+radon cc -s -a -i venv .
+popd
diff --git a/hack/check-python/measure-maintainability-index.sh b/hack/check-python/measure-maintainability-index.sh
new file mode 100755
index 00000000..2abb7e69
--- /dev/null
+++ b/hack/check-python/measure-maintainability-index.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+. ./hack/check-python/prepare-env.sh
+
+[ "$NOVENV" == "1" ] || prepare_venv || exit 1
+
+SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
+
+pushd "${SCRIPT_DIR}/.."
+radon mi -s -i venv .
+popd
diff --git a/hack/check-python/prepare-env.sh b/hack/check-python/prepare-env.sh
new file mode 100644
index 00000000..ee4c68b0
--- /dev/null
+++ b/hack/check-python/prepare-env.sh
@@ -0,0 +1,12 @@
+
+directories=${directories:-"./test/acceptance/features"}
+pass=0
+fail=0
+
+function prepare_venv() {
+    python3 -m venv venv && source venv/bin/activate
+    for req in $(find . -name 'requirements.txt'); do
+        python3 "$(which pip3)" install -q -r $req;
+    done
+    python3 "$(which pip3)" install -q pydocstyle pyflakes vulture radon
+}
diff --git a/hack/deploy-sbo-local.sh b/hack/deploy-sbo-local.sh
new file mode 100755
index 00000000..edbabd03
--- /dev/null
+++ b/hack/deploy-sbo-local.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+OPERATOR_NAMESPACE=${OPERATOR_NAMESPACE:-}
+ZAP_FLAGS=${ZAP_FLAGS:-}
+
+SBO_LOCAL_LOG=out/sbo-local.log
+
+killall operator-sdk service-binding-operator-local
+
+operator-sdk --verbose run --local --namespace="$OPERATOR_NAMESPACE" --operator-flags "$ZAP_FLAGS" > $SBO_LOCAL_LOG 2>&1 &
+
+SBO_PID=$!
+
+attempts=12
+while [ -z "$(grep 'Starting workers' $SBO_LOCAL_LOG)" ]; do
+    if [[ $attempts -ge 0 ]]; then
+        sleep 5
+        attempts=$((attempts-1))
+    else
+        echo "FAILED"
+        kill $SBO_PID
+        exit 1
+    fi
+done
+
+echo $SBO_PID
diff --git a/hack/deploy-sbo-operator-hub.sh b/hack/deploy-sbo-operator-hub.sh
new file mode 100755
index 00000000..21d66948
--- /dev/null
+++ b/hack/deploy-sbo-operator-hub.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+# FIXME: This is a placeholder for installing SBO via operator hub
+echo "FAILED"
diff --git a/pkg/controller/servicebindingrequest/annotations/annotations.go b/pkg/controller/servicebindingrequest/annotations/annotations.go
index f4c4ace4..b19e17b0 100644
--- a/pkg/controller/servicebindingrequest/annotations/annotations.go
+++ b/pkg/controller/servicebindingrequest/annotations/annotations.go
@@ -40,6 +40,11 @@ type result struct {
 	Type bindingType
 	// Path is the nested location the collected data can be found in the Data field.
 	Path string
+	// RawData contains the annotation data collected by an annotation handler
+	// inside a deep structure with its root being composed by the path where
+	// the external resource name was extracted and the path within the external
+	// resource.
+	RawData map[string]interface{}
 }
 
 // handler should be implemented by types that want to offer a mechanism to provide binding data to
diff --git a/pkg/controller/servicebindingrequest/annotations/errors.go b/pkg/controller/servicebindingrequest/annotations/errors.go
index 58a3770a..9e82586e 100644
--- a/pkg/controller/servicebindingrequest/annotations/errors.go
+++ b/pkg/controller/servicebindingrequest/annotations/errors.go
@@ -1,7 +1,6 @@
 package annotations
 
 import (
-	"errors"
 	"fmt"
 )
 
@@ -11,7 +10,11 @@ func (e invalidArgumentErr) Error() string {
 	return fmt.Sprintf("invalid argument value for path %q", string(e))
 }
 
-var resourceNameFieldNotFoundErr = errors.New("secret name field not found")
+type errResourceNameFieldNotFound string
+
+func (e errResourceNameFieldNotFound) Error() string {
+	return fmt.Sprintf("secret name field %q not found", string(e))
+}
 
 type unknownBindingTypeErr string
 
diff --git a/pkg/controller/servicebindingrequest/annotations/resource.go b/pkg/controller/servicebindingrequest/annotations/resource.go
index 619a18e4..ec309570 100644
--- a/pkg/controller/servicebindingrequest/annotations/resource.go
+++ b/pkg/controller/servicebindingrequest/annotations/resource.go
@@ -47,7 +47,7 @@ func discoverRelatedResourceName(obj map[string]interface{}, bi *bindingInfo) (s
 		strings.Split(bi.ResourceReferencePath, ".")...,
 	)
 	if !ok {
-		return "", resourceNameFieldNotFoundErr
+		return "", errResourceNameFieldNotFound(bi.ResourceReferencePath)
 	}
 	if err != nil {
 		return "", err
@@ -115,6 +115,14 @@ func (h *resourceHandler) Handle() (result, error) {
 		return result{}, err
 	}
 
+	// get resource's kind.
+	gvk, err := h.restMapper.KindFor(h.relatedGroupVersionResource)
+	if err != nil {
+		return result{}, err
+	}
+
+	outputPathParts := []string{strings.ToLower(gvk.Kind)}
+
 	if mapVal, ok := val.(map[string]interface{}); ok {
 		tmpVal := make(map[string]interface{})
 		for k, v := range mapVal {
@@ -130,6 +138,7 @@ func (h *resourceHandler) Handle() (result, error) {
 		if err != nil {
 			return result{}, err
 		}
+		outputPathParts = append(outputPathParts, nested.NewPath(h.bindingInfo.SourcePath).GetParts()...)
 	}
 
 	typ, err := discoverBindingType(h.bindingInfo.Value)
@@ -137,15 +146,11 @@ func (h *resourceHandler) Handle() (result, error) {
 		return result{}, err
 	}
 
-	// get resource's kind.
-	gvk, err := h.restMapper.KindFor(h.relatedGroupVersionResource)
-	if err != nil {
-		return result{}, err
-	}
-
 	// prefix the output path with the kind of the resource.
-	outputPath := strings.Join([]string{
-		strings.ToLower(gvk.Kind),
+	outputPath := strings.Join(outputPathParts, ".")
+
+	rawDataPath := strings.Join([]string{
+		h.bindingInfo.ResourceReferencePath,
 		h.bindingInfo.SourcePath,
 	}, ".")
 
@@ -153,6 +158,8 @@ func (h *resourceHandler) Handle() (result, error) {
 		Data: nested.ComposeValue(val, nested.NewPath(outputPath)),
 		Type: typ,
 		Path: outputPath,
+
+		RawData: nested.ComposeValue(val, nested.NewPath(rawDataPath)),
 	}, nil
 }
 
diff --git a/pkg/controller/servicebindingrequest/binder.go b/pkg/controller/servicebindingrequest/binder.go
index fc551069..97eccc61 100644
--- a/pkg/controller/servicebindingrequest/binder.go
+++ b/pkg/controller/servicebindingrequest/binder.go
@@ -2,11 +2,11 @@ package servicebindingrequest
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"strings"
 	"time"
 
+	"k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime/schema"
@@ -62,9 +62,6 @@ func (f extraFieldsModifierFunc) ModifyExtraFields(u *unstructured.Unstructured)
 	return f(u)
 }
 
-var emptyApplicationSelectorErr = errors.New("application ResourceRef or MatchLabel not found")
-var applicationNotFound = errors.New("Application is already deleted")
-
 // search objects based in Kind/APIVersion, which contain the labels defined in ApplicationSelector.
 func (b *binder) search() (*unstructured.UnstructuredList, error) {
 	// If Application name is present
@@ -73,7 +70,7 @@ func (b *binder) search() (*unstructured.UnstructuredList, error) {
 	} else if b.sbr.Spec.ApplicationSelector.LabelSelector != nil {
 		return b.getApplicationByLabelSelector()
 	} else {
-		return nil, emptyApplicationSelectorErr
+		return nil, errEmptyApplicationSelector
 	}
 }
 
@@ -87,11 +84,13 @@ func (b *binder) getApplicationByName() (*unstructured.UnstructuredList, error)
 	object, err := b.dynClient.Resource(gvr).Namespace(ns).
 		Get(b.sbr.Spec.ApplicationSelector.ResourceRef, metav1.GetOptions{})
 	if err != nil {
+		if errors.IsNotFound(err) {
+			return nil, errApplicationNotFound
+		}
 		return nil, err
 	}
 
-	objList := &unstructured.UnstructuredList{Items: []unstructured.Unstructured{*object}}
-	return objList, nil
+	return &unstructured.UnstructuredList{Items: []unstructured.Unstructured{*object}}, nil
 }
 
 func (b *binder) getApplicationByLabelSelector() (*unstructured.UnstructuredList, error) {
@@ -105,7 +104,17 @@ func (b *binder) getApplicationByLabelSelector() (*unstructured.UnstructuredList
 	opts := metav1.ListOptions{
 		LabelSelector: labels.Set(matchLabels).String(),
 	}
-	return b.dynClient.Resource(gvr).Namespace(ns).List(opts)
+
+	objList, err := b.dynClient.Resource(gvr).Namespace(ns).List(opts)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(objList.Items) == 0 {
+		return nil, errApplicationNotFound
+	}
+
+	return objList, nil
 }
 
 // extractSpecVolumes based on volume path, extract it unstructured. It can return error on trying
@@ -570,9 +579,6 @@ func (b *binder) remove(objs *unstructured.UnstructuredList) error {
 func (b *binder) unbind() error {
 	objs, err := b.search()
 	if err != nil {
-		if errors.Is(err, applicationNotFound) {
-			return nil
-		}
 		return err
 	}
 	return b.remove(objs)
@@ -583,9 +589,6 @@ func (b *binder) unbind() error {
 func (b *binder) bind() ([]*unstructured.Unstructured, error) {
 	objs, err := b.search()
 	if err != nil {
-		if errors.Is(err, applicationNotFound) {
-			return nil, nil
-		}
 		return nil, err
 	}
 	return b.update(objs)
diff --git a/pkg/controller/servicebindingrequest/envvars/envvars.go b/pkg/controller/servicebindingrequest/envvars/envvars.go
index 0aa7163b..be6b662c 100644
--- a/pkg/controller/servicebindingrequest/envvars/envvars.go
+++ b/pkg/controller/servicebindingrequest/envvars/envvars.go
@@ -65,6 +65,10 @@ func Build(obj interface{}, path ...string) (map[string]string, error) {
 		return buildString(strconv.FormatInt(val, 10), path), nil
 	case float64:
 		return buildString(strconv.FormatFloat(val, 'f', -1, 64), path), nil
+	case []interface{}:
+		return buildSliceOfInterface(val, path)
+	case bool:
+		return buildString(strconv.FormatBool(val), path), nil
 	default:
 		return nil, errUnsupportedType
 	}
@@ -104,6 +108,19 @@ func buildMap(obj map[string]interface{}, path []string) (map[string]string, err
 	return envVars, nil
 }
 
+// buildSliceOfInterface retrurns a slice containing environment variables for
+// all the leaves present in the given 'obj' slice.
+func buildSliceOfInterface(obj []interface{}, acc []string) (map[string]string, error) {
+	envVars := make(map[string]string)
+	for i, v := range obj {
+		k := strconv.Itoa(i)
+		if err := buildInner(acc, k, v, envVars); err != nil {
+			return nil, err
+		}
+	}
+	return envVars, nil
+}
+
 // buildSliceOfMap returns a map containing environment variables for all the
 // leaves present in the given `obj` slice.
 func buildSliceOfMap(obj []map[string]interface{}, acc []string) (map[string]string, error) {
diff --git a/pkg/controller/servicebindingrequest/nested/accumulator/accumulator.go b/pkg/controller/servicebindingrequest/nested/accumulator/accumulator.go
index ccd4d1a2..e2af7b6d 100644
--- a/pkg/controller/servicebindingrequest/nested/accumulator/accumulator.go
+++ b/pkg/controller/servicebindingrequest/nested/accumulator/accumulator.go
@@ -1,8 +1,6 @@
 package accumulator
 
 import (
-	"errors"
-
 	"github.com/imdario/mergo"
 )
 
@@ -11,9 +9,6 @@ const valuesKey = "values"
 // accumulator is a value accumulator.
 type accumulator map[string]interface{}
 
-// unsupportedTypeErr is returned when an unsupported type is encountered.
-var unsupportedTypeErr = errors.New("unsupported type")
-
 // Accumulate accumulates the `val` value. An error is returned in the case
 // `val` contains an unsupported type.
 func (a accumulator) Accumulate(val interface{}) error {
@@ -28,7 +23,7 @@ func (a accumulator) Accumulate(val interface{}) error {
 	case []map[string]interface{}, []string, []int:
 		b[valuesKey] = v
 	default:
-		return unsupportedTypeErr
+		b[valuesKey] = v
 	}
 	return mergo.Merge(&a, b, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTypeCheck)
 }
diff --git a/pkg/controller/servicebindingrequest/nested/path.go b/pkg/controller/servicebindingrequest/nested/path.go
index 05ffa33f..ba9d2501 100644
--- a/pkg/controller/servicebindingrequest/nested/path.go
+++ b/pkg/controller/servicebindingrequest/nested/path.go
@@ -72,6 +72,19 @@ func (p path) adjustedPath() path {
 	return p
 }
 
+// GetParts returns the path parts.
+//
+// For example, if the path contains the string 'a.b.c', returns a []string
+// containing 'a', 'b' and 'c'.
+func (p path) GetParts() []string {
+	var parts []string
+	clean := p.clean()
+	for _, f := range clean {
+		parts = append(parts, f.Name)
+	}
+	return parts
+}
+
 // lastField returns the last field from the receiver.
 func (p path) lastField() (field, bool) {
 	if len(p) > 0 {
diff --git a/pkg/controller/servicebindingrequest/reconciler.go b/pkg/controller/servicebindingrequest/reconciler.go
index b2d8600e..e364389c 100644
--- a/pkg/controller/servicebindingrequest/reconciler.go
+++ b/pkg/controller/servicebindingrequest/reconciler.go
@@ -3,6 +3,7 @@ package servicebindingrequest
 import (
 	"context"
 	"errors"
+	"sort"
 
 	conditionsv1 "github.com/openshift/custom-resource-status/conditions/v1"
 	corev1 "k8s.io/api/core/v1"
@@ -19,11 +20,19 @@ import (
 )
 
 const (
-	// BindingReady indicates that the binding succeeded
-	BindingReady conditionsv1.ConditionType = "Ready"
+	// CollectionReady indicates readiness for collection and persistance of intermediate manifests
+	CollectionReady conditionsv1.ConditionType = "CollectionReady"
+	// InjectionReady indicates readiness to change application manifests to use those intermediate manifests
+	// If status is true, it indicates that the binding succeeded
+	InjectionReady conditionsv1.ConditionType = "InjectionReady"
 	// EmptyServiceSelectorsReason is used when the ServiceBindingRequest has empty
 	// backingServiceSelectors.
 	EmptyServiceSelectorsReason = "EmptyServiceSelectors"
+	// EmptyApplicationSelectorReason is used when the ServiceBindingRequest has empty
+	// applicationSelector.
+	EmptyApplicationSelectorReason = "EmptyApplicationSelector"
+	// ApplicationNotFoundReason is used when the application is not found.
+	ApplicationNotFoundReason = "ApplicationNotFound"
 )
 
 // Reconciler reconciles a ServiceBindingRequest object
@@ -67,6 +76,21 @@ func (r *reconciler) getServiceBindingRequest(
 	return sbr, nil
 }
 
+// distinctServiceSelectors returns the distinct elements from the given service selector slice.
+func distinctServiceSelectors(selectors []v1alpha1.BackingServiceSelector) []v1alpha1.BackingServiceSelector {
+	distinct := make(map[v1alpha1.BackingServiceSelector]bool)
+	for _, v := range selectors {
+		distinct[v] = true
+	}
+
+	var output []v1alpha1.BackingServiceSelector
+	for k := range distinct {
+		output = append(output, k)
+	}
+
+	return output
+}
+
 // extractServiceSelectors returns a list of all BackingServiceSelector items from a
 // ServiceBindingRequest.
 //
@@ -84,7 +108,17 @@ func extractServiceSelectors(
 	if inSelectors != nil {
 		selectors = append(selectors, *inSelectors...)
 	}
-	return selectors
+
+	// FIXME(isuttonl): sorting selectors using name and namespace can be more robust.
+	sort.Slice(selectors, func(i, j int) bool {
+		a := selectors[i].ResourceRef < selectors[j].ResourceRef
+		b := selectors[j].Namespace != nil &&
+			selectors[i].Namespace != nil &&
+			*selectors[i].Namespace < *selectors[j].Namespace
+		return a && b
+	})
+
+	return distinctServiceSelectors(selectors)
 }
 
 // Reconcile a ServiceBindingRequest by the following steps:
@@ -108,7 +142,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err
 	// fetch and validate namespaced ServiceBindingRequest instance
 	sbr, err := r.getServiceBindingRequest(request.NamespacedName)
 	if err != nil {
-		if errors.Is(err, applicationNotFound) {
+		if errors.Is(err, errApplicationNotFound) {
 			logger.Info("SBR deleted after application deletion")
 			return done()
 		}
@@ -131,13 +165,22 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err
 
 	selectors := extractServiceSelectors(sbr)
 	if len(selectors) == 0 {
-		conditionsv1.SetStatusCondition(&sbr.Status.Conditions, conditionsv1.Condition{
-			Type:    BindingReady,
-			Status:  corev1.ConditionFalse,
-			Reason:  EmptyServiceSelectorsReason,
-			Message: "The spec.backingServiceSelectors field is empty.",
-		})
-		_, updateErr := updateServiceBindingRequestStatus(r.dynClient, sbr)
+		_, updateErr := updateServiceBindingRequestStatus(
+			r.dynClient,
+			sbr,
+			conditionsv1.Condition{
+				Type:    CollectionReady,
+				Status:  corev1.ConditionFalse,
+				Reason:  EmptyServiceSelectorsReason,
+				Message: errEmptyBackingServiceSelectors.Error(),
+			},
+			conditionsv1.Condition{
+				Type:    InjectionReady,
+				Status:  corev1.ConditionFalse,
+				Reason:  EmptyServiceSelectorsReason,
+				Message: errEmptyBackingServiceSelectors.Error(),
+			},
+		)
 		if updateErr == nil {
 			return done()
 		}
@@ -150,6 +193,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err
 	}
 
 	serviceCtxs, err := buildServiceContexts(
+		logger.WithName("buildServiceContexts"),
 		r.dynClient,
 		sbr.GetNamespace(),
 		selectors,
diff --git a/pkg/controller/servicebindingrequest/service.go b/pkg/controller/servicebindingrequest/service.go
index 49f867ec..bc7b2c18 100644
--- a/pkg/controller/servicebindingrequest/service.go
+++ b/pkg/controller/servicebindingrequest/service.go
@@ -24,6 +24,11 @@ var (
 	// errEmptyBackingServiceSelectors is returned when no backing service selectors have been
 	// informed in the Service Binding Request.
 	errEmptyBackingServiceSelectors = errors.New("backing service selectors are empty")
+	// errEmptyApplicationSelector is returned when no application selectors have been
+	// informed in the Service Binding Request.
+	errEmptyApplicationSelector = errors.New("application selectors are empty")
+	// errApplicationNotFound is returned when no application is found
+	errApplicationNotFound = errors.New("application not found")
 )
 
 func findService(
@@ -191,8 +196,15 @@ func buildOwnedResourceContext(
 	outputPath string,
 ) (*serviceContext, error) {
 	svcCtx, err := buildServiceContext(
-		client, obj.GetNamespace(), obj.GetObjectKind().GroupVersionKind(), obj.GetName(),
-		ownerEnvVarPrefix, restMapper, nil)
+		reconcilerLog.WithName("buildServiceContext"),
+		client,
+		obj.GetNamespace(),
+		obj.GetObjectKind().GroupVersionKind(),
+		obj.GetName(),
+		ownerEnvVarPrefix,
+		restMapper,
+		nil,
+	)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/controller/servicebindingrequest/servicebinder.go b/pkg/controller/servicebindingrequest/servicebinder.go
index 03e3aa32..3f48fa8c 100644
--- a/pkg/controller/servicebindingrequest/servicebinder.go
+++ b/pkg/controller/servicebindingrequest/servicebinder.go
@@ -2,6 +2,7 @@ package servicebindingrequest
 
 import (
 	"context"
+	"errors"
 	"fmt"
 
 	conditionsv1 "github.com/openshift/custom-resource-status/conditions/v1"
@@ -153,7 +154,7 @@ func (b *serviceBinder) unbind() (reconcile.Result, error) {
 	}
 
 	logger.Debug("Removing resource finalizers...")
-	b.sbr.SetFinalizers(removeStringSlice(b.sbr.GetFinalizers(), finalizer))
+	removeFinalizer(b.sbr)
 	if _, err := b.updateServiceBindingRequest(b.sbr); err != nil {
 		return noRequeue(err)
 	}
@@ -166,7 +167,11 @@ func (b *serviceBinder) unbind() (reconcile.Result, error) {
 func updateServiceBindingRequestStatus(
 	dynClient dynamic.Interface,
 	sbr *v1alpha1.ServiceBindingRequest,
+	conditions ...conditionsv1.Condition,
 ) (*v1alpha1.ServiceBindingRequest, error) {
+	for _, v := range conditions {
+		conditionsv1.SetStatusCondition(&sbr.Status.Conditions, v)
+	}
 	u, err := converter.ToUnstructured(sbr)
 	if err != nil {
 		return nil, err
@@ -222,7 +227,7 @@ func (b *serviceBinder) onError(
 		b.setApplicationObjects(sbrStatus, objs)
 	}
 	conditionsv1.SetStatusCondition(&sbrStatus.Conditions, conditionsv1.Condition{
-		Type:    BindingReady,
+		Type:    InjectionReady,
 		Status:  corev1.ConditionFalse,
 		Reason:  bindingFail,
 		Message: b.message(err),
@@ -236,6 +241,62 @@ func (b *serviceBinder) onError(
 	return requeueOnNotFound(err, requeueAfter)
 }
 
+// isApplicationSelectorEmpty returns true if applicationSelector is not declared in
+// the Service Binding Request.
+func isApplicationSelectorEmpty(
+	application v1alpha1.ApplicationSelector,
+) bool {
+	var emptyApplication v1alpha1.ApplicationSelector
+	return application == emptyApplication ||
+		application.ResourceRef == "" &&
+			application.LabelSelector.MatchLabels == nil
+}
+
+func addFinalizer(sbr *v1alpha1.ServiceBindingRequest) {
+	sbr.SetFinalizers(append(removeStringSlice(sbr.GetFinalizers(), finalizer), finalizer))
+}
+
+func removeFinalizer(sbr *v1alpha1.ServiceBindingRequest) {
+	sbr.SetFinalizers(removeStringSlice(sbr.GetFinalizers(), finalizer))
+}
+
+// handleApplicationError handles scenarios when:
+// 1. applicationSelector not declared in the Service Binding Request
+// 2. application not found
+func (b *serviceBinder) handleApplicationError(reason string, applicationError error, sbrStatus *v1alpha1.ServiceBindingRequestStatus) (reconcile.Result, error) {
+	conditionsv1.SetStatusCondition(&sbrStatus.Conditions, conditionsv1.Condition{
+		Type:    InjectionReady,
+		Status:  corev1.ConditionFalse,
+		Reason:  reason,
+		Message: applicationError.Error(),
+	})
+
+	// updating status of request instance
+	sbr, err := b.updateStatusServiceBindingRequest(b.sbr, sbrStatus)
+	if err != nil {
+		return requeueError(err)
+	}
+
+	// appending finalizer, should be later removed upon resource deletion
+	addFinalizer(b.sbr)
+	if _, err = b.updateServiceBindingRequest(sbr); err != nil {
+		return requeueError(err)
+	}
+
+	b.logger.Info(applicationError.Error())
+
+	if errors.Is(applicationError, errApplicationNotFound) {
+		removeFinalizer(b.sbr)
+		if _, err = b.updateServiceBindingRequest(sbr); err != nil {
+			return requeueError(err)
+		}
+		return requeue(applicationError, requeueAfter)
+
+	}
+
+	return done()
+}
+
 // bind configures binding between the Service Binding Request and its related objects.
 func (b *serviceBinder) bind() (reconcile.Result, error) {
 	sbrStatus := b.sbr.Status.DeepCopy()
@@ -249,10 +310,21 @@ func (b *serviceBinder) bind() (reconcile.Result, error) {
 	}
 	sbrStatus.Secret = secretObj.GetName()
 
+	conditionsv1.SetStatusCondition(&sbrStatus.Conditions, conditionsv1.Condition{
+		Type:   CollectionReady,
+		Status: corev1.ConditionTrue,
+	})
+
+	if isApplicationSelectorEmpty(b.sbr.Spec.ApplicationSelector) {
+		return b.handleApplicationError(EmptyApplicationSelectorReason, errEmptyApplicationSelector, sbrStatus)
+	}
 	updatedObjects, err := b.binder.bind()
 	if err != nil {
 		b.logger.Error(err, "On binding application.")
-		return b.onError(err, b.sbr, sbrStatus, updatedObjects)
+		if errors.Is(err, errApplicationNotFound) {
+			return b.handleApplicationError(ApplicationNotFoundReason, errApplicationNotFound, sbrStatus)
+		}
+		return b.onError(err, b.sbr, sbrStatus, nil)
 	}
 	b.setApplicationObjects(sbrStatus, updatedObjects)
 
@@ -264,7 +336,7 @@ func (b *serviceBinder) bind() (reconcile.Result, error) {
 	}
 
 	conditionsv1.SetStatusCondition(&sbrStatus.Conditions, conditionsv1.Condition{
-		Type:   BindingReady,
+		Type:   InjectionReady,
 		Status: corev1.ConditionTrue,
 	})
 
@@ -275,9 +347,10 @@ func (b *serviceBinder) bind() (reconcile.Result, error) {
 	}
 
 	// appending finalizer, should be later removed upon resource deletion
-	sbr.SetFinalizers(append(removeStringSlice(b.sbr.GetFinalizers(), finalizer), finalizer))
+	addFinalizer(sbr)
+
 	if _, err = b.updateServiceBindingRequest(sbr); err != nil {
-		return noRequeue(err)
+		return requeueError(err)
 	}
 
 	b.logger.Info("All done!")
diff --git a/pkg/controller/servicebindingrequest/servicecontext.go b/pkg/controller/servicebindingrequest/servicecontext.go
index 5696a17f..df4fa81e 100644
--- a/pkg/controller/servicebindingrequest/servicecontext.go
+++ b/pkg/controller/servicebindingrequest/servicecontext.go
@@ -11,6 +11,7 @@ import (
 	"k8s.io/client-go/dynamic"
 
 	v1alpha1 "github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1"
+	"github.com/redhat-developer/service-binding-operator/pkg/log"
 )
 
 // serviceContext contains information related to a service.
@@ -49,6 +50,7 @@ func stringValueOrDefault(val *string, defaultVal string) string {
 // buildServiceContexts return a collection of ServiceContext values from the given service
 // selectors.
 func buildServiceContexts(
+	logger *log.Log,
 	client dynamic.Interface,
 	defaultNs string,
 	selectors []v1alpha1.BackingServiceSelector,
@@ -56,12 +58,22 @@ func buildServiceContexts(
 	restMapper meta.RESTMapper,
 ) (serviceContextList, error) {
 	svcCtxs := make(serviceContextList, 0)
+
+SELECTORS:
 	for _, s := range selectors {
 		ns := stringValueOrDefault(s.Namespace, defaultNs)
 		gvk := schema.GroupVersionKind{Kind: s.Kind, Version: s.Version, Group: s.Group}
-		svcCtx, err := buildServiceContext(
-			client, ns, gvk, s.ResourceRef, s.EnvVarPrefix, restMapper, s.Id)
+		svcCtx, err := buildServiceContext(logger.WithName("buildServiceContexts"), client, ns, gvk,
+			s.ResourceRef, s.EnvVarPrefix, restMapper, s.Id)
+
 		if err != nil {
+			// best effort approach; should not break in common cases such as a unknown annotation
+			// prefix (other annotations might exist in the resource) or, in the case of a valid
+			// annotation, the handler expected for the annotation can't be found.
+			if err == annotations.ErrInvalidAnnotationPrefix || annotations.IsErrHandlerNotFound(err) {
+				logger.Trace("Continuing to next selector", "Error", err)
+				continue SELECTORS
+			}
 			return nil, err
 		}
 		svcCtxs = append(svcCtxs, svcCtx)
@@ -119,10 +131,64 @@ func findOwnedResourcesCtxs(
 	)
 }
 
+func merge(dst map[string]interface{}, src map[string]interface{}) (map[string]interface{}, error) {
+	merged := map[string]interface{}{}
+
+	err := mergo.Merge(&merged, src, mergo.WithOverride, mergo.WithOverrideEmptySlice)
+	if err != nil {
+		return nil, err
+	}
+
+	err = mergo.Merge(&merged, dst)
+	if err != nil {
+		return nil, err
+	}
+
+	return merged, nil
+}
+
+func runHandler(
+	client dynamic.Interface,
+	obj *unstructured.Unstructured,
+	outputObj *unstructured.Unstructured,
+	key string,
+	value string,
+	envVars map[string]interface{},
+	volumeKeys *[]string,
+	restMapper meta.RESTMapper,
+) error {
+	h, err := annotations.BuildHandler(client, obj, key, value, restMapper)
+	if err != nil {
+		return err
+	}
+	r, err := h.Handle()
+	if err != nil {
+		return err
+	}
+
+	if newObj, err := merge(outputObj.Object, r.RawData); err != nil {
+		return err
+	} else {
+		outputObj.Object = newObj
+	}
+
+	err = mergo.Merge(&envVars, r.Data, mergo.WithAppendSlice, mergo.WithOverride)
+	if err != nil {
+		return err
+	}
+
+	if r.Type == annotations.BindingTypeVolumeMount {
+		*volumeKeys = []string(append(*volumeKeys, r.Path))
+	}
+
+	return nil
+}
+
 // buildServiceContext inspects g the API server searching for the service resources, associated CRD
 // and OLM's CRDDescription if present, and processes those with relevant annotations to compose a
 // ServiceContext.
 func buildServiceContext(
+	logger *log.Log,
 	client dynamic.Interface,
 	ns string,
 	gvk schema.GroupVersionKind,
@@ -171,37 +237,19 @@ func buildServiceContext(
 	volumeKeys := make([]string, 0)
 	envVars := make(map[string]interface{})
 
-	for annotationKey, annotationValue := range anns {
-		h, err := annotations.BuildHandler(
-			client,
-			obj,
-			annotationKey,
-			annotationValue,
-			restMapper,
-		)
-		if err != nil {
-			if err == annotations.ErrInvalidAnnotationPrefix || annotations.IsErrHandlerNotFound(err) {
-				continue
-			}
-			return nil, err
-		}
-		r, err := h.Handle()
-		if err != nil {
-			continue
-		}
+	// outputObj will be used to keep the changes processed by the handler.
+	outputObj := obj.DeepCopy()
 
-		err = mergo.Merge(&envVars, r.Data, mergo.WithAppendSlice, mergo.WithOverride)
+	for k, v := range anns {
+		// runHandler modifies 'outputObj', 'envVars' and 'volumeKeys' in place.
+		err := runHandler(client, obj, outputObj, k, v, envVars, &volumeKeys, restMapper)
 		if err != nil {
-			return nil, err
-		}
-
-		if r.Type == annotations.BindingTypeVolumeMount {
-			volumeKeys = append(volumeKeys, r.Path)
+			logger.Debug("Failed executing runHandler", "Error", err)
 		}
 	}
 
 	serviceCtx := &serviceContext{
-		service:      obj,
+		service:      outputObj,
 		envVars:      envVars,
 		volumeKeys:   volumeKeys,
 		envVarPrefix: envVarPrefix,
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 00000000..6ecd48d4
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,3 @@
+[pycodestyle]
+max-line-length = 160
+statistics = True


Test patch:
diff --git a/pkg/controller/servicebindingrequest/annotations/configmap_test.go b/pkg/controller/servicebindingrequest/annotations/configmap_test.go
index c71d5186..d717f805 100644
--- a/pkg/controller/servicebindingrequest/annotations/configmap_test.go
+++ b/pkg/controller/servicebindingrequest/annotations/configmap_test.go
@@ -110,12 +110,8 @@ func TestConfigMapHandler(t *testing.T) {
 		},
 		expected: map[string]interface{}{
 			"configmap": map[string]interface{}{
-				"status": map[string]interface{}{
-					"dbCredentials": map[string]interface{}{
-						"username": "AzureDiamond",
-						"password": "hunter2",
-					},
-				},
+				"username": "AzureDiamond",
+				"password": "hunter2",
 			},
 		},
 	}))
diff --git a/pkg/controller/servicebindingrequest/annotations/secret_test.go b/pkg/controller/servicebindingrequest/annotations/secret_test.go
index 73f7494a..f8b5d596 100644
--- a/pkg/controller/servicebindingrequest/annotations/secret_test.go
+++ b/pkg/controller/servicebindingrequest/annotations/secret_test.go
@@ -109,12 +109,8 @@ func TestSecretHandler(t *testing.T) {
 		},
 		expected: map[string]interface{}{
 			"secret": map[string]interface{}{
-				"status": map[string]interface{}{
-					"dbCredentials": map[string]interface{}{
-						"username": "AzureDiamond",
-						"password": "hunter2",
-					},
-				},
+				"username": "AzureDiamond",
+				"password": "hunter2",
 			},
 		},
 	}))
diff --git a/pkg/controller/servicebindingrequest/reconciler_test.go b/pkg/controller/servicebindingrequest/reconciler_test.go
index 5206e31a..0509a9c9 100644
--- a/pkg/controller/servicebindingrequest/reconciler_test.go
+++ b/pkg/controller/servicebindingrequest/reconciler_test.go
@@ -50,6 +50,32 @@ func reconcileRequest() reconcile.Request {
 	}
 }
 
+func requireConditionPresentAndTrue(t *testing.T, condition conditionsv1.ConditionType, sbrConditions []conditionsv1.Condition) {
+	require.True(t,
+		conditionsv1.IsStatusConditionPresentAndEqual(
+			sbrConditions,
+			condition,
+			corev1.ConditionTrue,
+		),
+		"%+v should exist and be true; existing conditions: %+v",
+		condition,
+		sbrConditions,
+	)
+}
+
+func requireConditionPresentAndFalse(t *testing.T, condition conditionsv1.ConditionType, sbrConditions []conditionsv1.Condition) {
+	require.True(t,
+		conditionsv1.IsStatusConditionPresentAndEqual(
+			sbrConditions,
+			condition,
+			corev1.ConditionFalse,
+		),
+		"%+v should exist and be false; existing conditions: %+v",
+		condition,
+		sbrConditions,
+	)
+}
+
 type fakeResourceWatcher struct {
 	record map[schema.GroupVersionKind]bool
 	mapper meta.RESTMapper
@@ -105,8 +131,9 @@ func TestApplicationSelectorByName(t *testing.T) {
 		sbrOutput, err := r.getServiceBindingRequest(namespacedName)
 		require.NoError(t, err)
 
-		require.Equal(t, BindingReady, sbrOutput.Status.Conditions[0].Type)
-		require.Equal(t, corev1.ConditionTrue, sbrOutput.Status.Conditions[0].Status)
+		requireConditionPresentAndTrue(t, CollectionReady, sbrOutput.Status.Conditions)
+		requireConditionPresentAndTrue(t, InjectionReady, sbrOutput.Status.Conditions)
+
 		require.Equal(t, 1, len(sbrOutput.Status.Applications))
 		expectedStatus := v1alpha1.BoundApplication{
 			GroupVersionKind: v1.GroupVersionKind{
@@ -167,8 +194,9 @@ func TestReconcilerReconcileUsingSecret(t *testing.T) {
 		sbrOutput, err := r.getServiceBindingRequest(namespacedName)
 		require.NoError(t, err)
 
-		require.Equal(t, BindingReady, sbrOutput.Status.Conditions[0].Type)
-		require.Equal(t, corev1.ConditionTrue, sbrOutput.Status.Conditions[0].Status)
+		requireConditionPresentAndTrue(t, CollectionReady, sbrOutput.Status.Conditions)
+		requireConditionPresentAndTrue(t, InjectionReady, sbrOutput.Status.Conditions)
+
 		require.Equal(t, reconcilerName, sbrOutput.Status.Secret)
 
 		require.Equal(t, 1, len(sbrOutput.Status.Applications))
@@ -229,7 +257,7 @@ func TestReconcilerReconcileUsingVolumes(t *testing.T) {
 	})
 }
 
-func TestReconcilerGenericBinding(t *testing.T) {
+func TestApplicationNotFound(t *testing.T) {
 	backingServiceResourceRef := "backingService1"
 	matchLabels := map[string]string{
 		"connects-to": "database",
@@ -249,22 +277,15 @@ func TestReconcilerGenericBinding(t *testing.T) {
 
 	// Reconcile without deployment
 	res, err := r.Reconcile(reconcileRequest())
-	require.NoError(t, err)
-	require.False(t, res.Requeue)
+	require.EqualError(t, err, errApplicationNotFound.Error())
+	require.True(t, res.Requeue)
 
 	namespacedName := types.NamespacedName{Namespace: reconcilerNs, Name: reconcilerName}
 	sbrOutput, err := r.getServiceBindingRequest(namespacedName)
 	require.NoError(t, err)
 
-	require.True(t,
-		conditionsv1.IsStatusConditionPresentAndEqual(
-			sbrOutput.Status.Conditions,
-			BindingReady,
-			corev1.ConditionTrue,
-		),
-		"Ready condition should exist and true; existing conditions: %+v",
-		sbrOutput.Status.Conditions,
-	)
+	requireConditionPresentAndTrue(t, CollectionReady, sbrOutput.Status.Conditions)
+	requireConditionPresentAndFalse(t, InjectionReady, sbrOutput.Status.Conditions)
 	require.Len(t, sbrOutput.Status.Applications, 0)
 
 	// Reconcile with deployment
@@ -286,8 +307,9 @@ func TestReconcilerGenericBinding(t *testing.T) {
 	sbrOutput2, err := r.getServiceBindingRequest(namespacedName)
 	require.NoError(t, err)
 
-	require.Equal(t, BindingReady, sbrOutput2.Status.Conditions[0].Type)
-	require.Equal(t, corev1.ConditionTrue, sbrOutput2.Status.Conditions[0].Status)
+	requireConditionPresentAndTrue(t, CollectionReady, sbrOutput2.Status.Conditions)
+	requireConditionPresentAndTrue(t, InjectionReady, sbrOutput2.Status.Conditions)
+
 	require.Equal(t, reconcilerName, sbrOutput2.Status.Secret)
 	require.Equal(t, 1, len(sbrOutput2.Status.Applications))
 
@@ -313,8 +335,17 @@ func TestReconcilerGenericBinding(t *testing.T) {
 
 	sbrOutput3, err := r.getServiceBindingRequest(namespacedName)
 	require.NoError(t, err)
-	require.Equal(t, BindingReady, sbrOutput3.Status.Conditions[0].Type)
-	require.Equal(t, corev1.ConditionTrue, sbrOutput3.Status.Conditions[0].Status)
+
+	u, err = fakeDynClient.Resource(deploymentsGVR).Get(reconcilerName, metav1.GetOptions{})
+	require.NoError(t, err)
+
+	d = appsv1.Deployment{}
+	err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &d)
+	require.NoError(t, err)
+
+	requireConditionPresentAndTrue(t, CollectionReady, sbrOutput3.Status.Conditions)
+	requireConditionPresentAndTrue(t, InjectionReady, sbrOutput3.Status.Conditions)
+
 	require.Equal(t, reconcilerName, sbrOutput3.Status.Secret)
 	require.Equal(t, s.Data["password"], []byte("abc123"))
 	require.Equal(t, 1, len(sbrOutput3.Status.Applications))
@@ -365,8 +396,9 @@ func TestReconcilerReconcileWithConflictingAppSelc(t *testing.T) {
 			},
 		}
 
-		require.Equal(t, BindingReady, sbrOutput.Status.Conditions[0].Type)
-		require.Equal(t, corev1.ConditionTrue, sbrOutput.Status.Conditions[0].Status)
+		requireConditionPresentAndTrue(t, CollectionReady, sbrOutput.Status.Conditions)
+		requireConditionPresentAndTrue(t, InjectionReady, sbrOutput.Status.Conditions)
+
 		require.Equal(t, reconcilerName, sbrOutput.Status.Secret)
 		require.Len(t, sbrOutput.Status.Applications, 1)
 		require.True(t, reflect.DeepEqual(expectedStatus, sbrOutput.Status.Applications[0]))
@@ -393,11 +425,8 @@ func TestEmptyApplicationSelector(t *testing.T) {
 	sbrOutput, err := r.getServiceBindingRequest(namespacedName)
 	require.NoError(t, err)
 
-	require.Equal(t, BindingReady, sbrOutput.Status.Conditions[0].Type)
-	// Currently the Conditions[0].Status would be true as application's absence won't cause error
-	// TODO New steps to conditions to be introduced - InjectionReady, CollectionReady
-	require.Equal(t, corev1.ConditionTrue, sbrOutput.Status.Conditions[0].Status)
-	require.Equal(t, 0, len(sbrOutput.Status.Applications))
+	requireConditionPresentAndTrue(t, CollectionReady, sbrOutput.Status.Conditions)
+	requireConditionPresentAndFalse(t, InjectionReady, sbrOutput.Status.Conditions)
 }
 
 func TestBindTwoSbrsWithSingleApplication(t *testing.T) {
@@ -452,8 +481,8 @@ func TestBindTwoSbrsWithSingleApplication(t *testing.T) {
 			},
 		}
 
-		require.Equal(t, BindingReady, sbrOutput.Status.Conditions[0].Type)
-		require.Equal(t, corev1.ConditionTrue, sbrOutput.Status.Conditions[0].Status)
+		requireConditionPresentAndTrue(t, CollectionReady, sbrOutput.Status.Conditions)
+		requireConditionPresentAndTrue(t, InjectionReady, sbrOutput.Status.Conditions)
 		require.Equal(t, sbrName1, sbrOutput.Status.Secret)
 		require.Len(t, sbrOutput.Status.Applications, 1)
 		require.True(t, reflect.DeepEqual(expectedStatus, sbrOutput.Status.Applications[0]))
@@ -489,8 +518,8 @@ func TestBindTwoSbrsWithSingleApplication(t *testing.T) {
 			},
 		}
 
-		require.Equal(t, BindingReady, sbrOutput.Status.Conditions[0].Type)
-		require.Equal(t, corev1.ConditionTrue, sbrOutput.Status.Conditions[0].Status)
+		requireConditionPresentAndTrue(t, CollectionReady, sbrOutput.Status.Conditions)
+		requireConditionPresentAndTrue(t, InjectionReady, sbrOutput.Status.Conditions)
 		require.Equal(t, sbrName2, sbrOutput.Status.Secret)
 		require.Len(t, sbrOutput.Status.Applications, 1)
 		require.True(t, reflect.DeepEqual(expectedStatus, sbrOutput.Status.Applications[0]))
diff --git a/pkg/controller/servicebindingrequest/retriever_test.go b/pkg/controller/servicebindingrequest/retriever_test.go
index 38ecb146..41bc3cad 100644
--- a/pkg/controller/servicebindingrequest/retriever_test.go
+++ b/pkg/controller/servicebindingrequest/retriever_test.go
@@ -37,7 +37,7 @@ func TestRetrieverProcessServiceContexts(t *testing.T) {
 
 	f := mocks.NewFake(t, ns)
 	f.AddMockedUnstructuredCSV("csv")
-	f.AddNamespacedMockedSecret("db-credentials", backingServiceNs)
+	f.AddNamespacedMockedSecret("db-credentials", backingServiceNs, nil)
 
 	cr, err := mocks.UnstructuredDatabaseCRMock(backingServiceNs, crName)
 	require.NoError(t, err)
diff --git a/pkg/controller/servicebindingrequest/service_test.go b/pkg/controller/servicebindingrequest/service_test.go
index a2b9d951..eedf5895 100644
--- a/pkg/controller/servicebindingrequest/service_test.go
+++ b/pkg/controller/servicebindingrequest/service_test.go
@@ -50,7 +50,7 @@ func TestPlannerWithExplicitBackingServiceNamespace(t *testing.T) {
 	f.AddMockedUnstructuredDatabaseCRD()
 	f.AddMockedUnstructuredCSV("cluster-service-version")
 	db := f.AddMockedDatabaseCR(resourceRef, backingServiceNamespace)
-	f.AddNamespacedMockedSecret("db-credentials", backingServiceNamespace)
+	f.AddNamespacedMockedSecret("db-credentials", backingServiceNamespace, nil)
 
 	t.Run("findService", func(t *testing.T) {
 		cr, err := findService(
diff --git a/pkg/controller/servicebindingrequest/servicebinder_test.go b/pkg/controller/servicebindingrequest/servicebinder_test.go
index 64fe9ba1..444d084a 100644
--- a/pkg/controller/servicebindingrequest/servicebinder_test.go
+++ b/pkg/controller/servicebindingrequest/servicebinder_test.go
@@ -105,31 +105,14 @@ func TestServiceBinder_Bind(t *testing.T) {
 			if len(args.wantConditions) > 0 {
 				// proceed to find whether conditions match wanted conditions
 				for _, c := range args.wantConditions {
-					for _, cond := range sb.sbr.Status.Conditions {
-						expected := conditionsv1.Condition{}
-						got := conditionsv1.Condition{}
-						if len(c.Type) > 0 {
-							expected.Type = c.Type
-							got.Type = cond.Type
-						}
-						if len(c.Status) > 0 {
-							expected.Status = c.Status
-							got.Status = cond.Status
-						}
-						if len(c.Reason) > 0 {
-							expected.Reason = c.Reason
-							got.Reason = cond.Reason
-						}
-						if len(c.Message) > 0 {
-							expected.Message = c.Message
-							got.Message = cond.Message
-						}
-
-						require.Equal(t, expected, got)
+					if c.Status == corev1.ConditionTrue {
+						requireConditionPresentAndTrue(t, c.Type, sb.sbr.Status.Conditions)
+					}
+					if c.Status == corev1.ConditionFalse {
+						requireConditionPresentAndFalse(t, c.Type, sb.sbr.Status.Conditions)
 					}
 				}
 			}
-
 			// regardless of the result, verify the actions expected by the reconciliation
 			// process have been issued if user has specified wanted actions
 			if len(args.wantActions) > 0 {
@@ -409,7 +392,11 @@ func TestServiceBinder_Bind(t *testing.T) {
 		},
 		wantConditions: []wantedCondition{
 			{
-				Type:   BindingReady,
+				Type:   CollectionReady,
+				Status: corev1.ConditionTrue,
+			},
+			{
+				Type:   InjectionReady,
 				Status: corev1.ConditionTrue,
 			},
 		},
@@ -448,7 +435,11 @@ func TestServiceBinder_Bind(t *testing.T) {
 		},
 		wantConditions: []wantedCondition{
 			{
-				Type:   BindingReady,
+				Type:   CollectionReady,
+				Status: corev1.ConditionTrue,
+			},
+			{
+				Type:   InjectionReady,
 				Status: corev1.ConditionTrue,
 			},
 		},
@@ -488,7 +479,11 @@ func TestServiceBinder_Bind(t *testing.T) {
 		},
 		wantConditions: []wantedCondition{
 			{
-				Type:   BindingReady,
+				Type:   CollectionReady,
+				Status: corev1.ConditionTrue,
+			},
+			{
+				Type:   InjectionReady,
 				Status: corev1.ConditionTrue,
 			},
 		},
@@ -506,13 +501,16 @@ func TestServiceBinder_Bind(t *testing.T) {
 			},
 			restMapper: testutils.BuildTestRESTMapper(),
 		},
-		wantErr: emptyApplicationSelectorErr,
 		wantConditions: []wantedCondition{
 			{
-				Type:    BindingReady,
+				Type:   CollectionReady,
+				Status: corev1.ConditionTrue,
+			},
+			{
+				Type:    InjectionReady,
 				Status:  corev1.ConditionFalse,
-				Reason:  bindingFail,
-				Message: emptyApplicationSelectorErr.Error(),
+				Reason:  EmptyApplicationSelectorReason,
+				Message: errEmptyApplicationSelector.Error(),
 			},
 		},
 	}))
@@ -543,7 +541,11 @@ func TestServiceBinder_Bind(t *testing.T) {
 		},
 		wantConditions: []wantedCondition{
 			{
-				Type:   BindingReady,
+				Type:   CollectionReady,
+				Status: corev1.ConditionTrue,
+			},
+			{
+				Type:   InjectionReady,
 				Status: corev1.ConditionTrue,
 			},
 		},
diff --git a/pkg/controller/servicebindingrequest/servicecontext_test.go b/pkg/controller/servicebindingrequest/servicecontext_test.go
index cfd323eb..cb9324ac 100644
--- a/pkg/controller/servicebindingrequest/servicecontext_test.go
+++ b/pkg/controller/servicebindingrequest/servicecontext_test.go
@@ -6,6 +6,7 @@ import (
 	routev1 "github.com/openshift/api/route/v1"
 	pgv1alpha1 "github.com/operator-backing-service-samples/postgresql-operator/pkg/apis/postgresql/v1alpha1"
 	"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1"
+	"github.com/redhat-developer/service-binding-operator/pkg/log"
 	"github.com/redhat-developer/service-binding-operator/pkg/testutils"
 	"github.com/redhat-developer/service-binding-operator/test/mocks"
 	"github.com/stretchr/testify/require"
@@ -15,43 +16,130 @@ import (
 )
 
 func TestBuildServiceContexts(t *testing.T) {
-	ns := "planner"
-	name := "service-binding-request"
-	resourceRef := "db-testing"
-	matchLabels := map[string]string{
-		"connects-to": "database",
-		"environment": "planner",
-	}
-	f := mocks.NewFake(t, ns)
-	sbr := f.AddMockedServiceBindingRequest(name, nil, resourceRef, "", deploymentsGVR, matchLabels)
-	sbr.Spec.BackingServiceSelectors = &[]v1alpha1.BackingServiceSelector{
-		*sbr.Spec.BackingServiceSelector,
-	}
-	f.AddMockedUnstructuredCSV("cluster-service-version")
-	f.AddMockedDatabaseCR(resourceRef, ns)
-	f.AddMockedUnstructuredDatabaseCRD()
-	f.AddMockedUnstructuredSecret("db-credentials")
-
+	logger := log.NewLog("testBuildServiceContexts")
 	restMapper := testutils.BuildTestRESTMapper()
 
-	t.Run("existing selectors", func(t *testing.T) {
+	t.Run("empty selectors", func(t *testing.T) {
+		ns := "planner"
+		f := mocks.NewFake(t, ns)
 		serviceCtxs, err := buildServiceContexts(
-			f.FakeDynClient(), ns, extractServiceSelectors(sbr), false, restMapper)
-		require.NoError(t, err)
-		require.NotEmpty(t, serviceCtxs)
+			logger, f.FakeDynClient(), ns, nil, false, restMapper)
+
+		require.NoError(t, err, "buildServiceContexts must execute without errors")
+		require.Empty(t, serviceCtxs, "buildServiceContexts must be empty")
 	})
 
-	t.Run("empty selectors", func(t *testing.T) {
-		serviceCtxs, err := buildServiceContexts(f.FakeDynClient(), ns, nil, false, restMapper)
-		require.NoError(t, err)
-		require.Empty(t, serviceCtxs)
+	t.Run("declared and existing selectors", func(t *testing.T) {
+		sbrName := "service-binding-request"
+		firstResourceRef := "db-testing"
+		firstNamespace := "existing-namespace"
+		matchLabels := map[string]string{
+			"connects-to": "database",
+			"environment": "planner",
+		}
+
+		f := mocks.NewFake(t, firstNamespace)
+		f.AddMockedUnstructuredCSV("cluster-service-version")
+		f.AddMockedDatabaseCR(firstResourceRef, firstNamespace)
+		f.AddMockedUnstructuredDatabaseCRD()
+		f.AddNamespacedMockedSecret("db-credentials", firstNamespace, nil)
+
+		sbr := f.AddMockedServiceBindingRequest(sbrName, nil, firstResourceRef, "", deploymentsGVR, matchLabels)
+
+		serviceCtxs, err := buildServiceContexts(
+			logger, f.FakeDynClient(), firstNamespace, extractServiceSelectors(sbr), false, restMapper)
+
+		require.NoError(t, err, "buildServiceContexts must execute without errors")
+		require.Len(t, serviceCtxs, 1, "buildServiceContexts must return only one item")
+
+		serviceCtx := serviceCtxs[0]
+		expectedKeys := []string{"status", "dbCredentials"}
+		expectedDbCredentials := map[string]interface{}{
+			"username": "user",
+			"password": "password",
+		}
+
+		gotDbCredentials, ok, err :=
+			unstructured.NestedFieldCopy(serviceCtx.service.Object, expectedKeys...)
+		require.NoError(t, err, "must not return error while copying status.dbCredentials out of the context's service")
+		require.True(t, ok, "status.dbCredentials must exist")
+		require.Equal(t, expectedDbCredentials, gotDbCredentials, "status.dbCredentials in context must be equal to expected")
 	})
 
 	t.Run("services in different namespace", func(t *testing.T) {
+		sameNs := "same-ns"
+		sameNsResourceRef := "same-ns-database"
+
+		otherNs := "other-ns"
+		otherNsResourceRef := "other-ns-database"
+
+		matchLabels := map[string]string{
+			"connects-to": "database",
+			"environment": "planner",
+		}
+
+		f := mocks.NewFake(t, sameNs)
+		f.AddMockedUnstructuredDatabaseCRD()
+		f.AddMockedDatabaseCR(sameNsResourceRef, sameNs)
+		f.AddNamespacedMockedSecret("db-credentials", sameNs, map[string][]byte{
+			"username": []byte("same-ns-username"),
+			"password": []byte("same-ns-password"),
+		})
+
+		f.AddMockedDatabaseCR(otherNsResourceRef, otherNs)
+		f.AddNamespacedMockedSecret("db-credentials", otherNs, map[string][]byte{
+			"username": []byte("other-ns-username"),
+			"password": []byte("other-ns-password"),
+		})
+
+		sbrName := "services-in-different-ns"
+
+		sbr := f.AddMockedServiceBindingRequest(sbrName, &sameNs, sameNsResourceRef, "", deploymentsGVR, matchLabels)
+		sbr.Spec.BackingServiceSelectors = &[]v1alpha1.BackingServiceSelector{
+			{
+				GroupVersionKind: metav1.GroupVersionKind{
+					Group:   mocks.CRDName,
+					Version: mocks.CRDVersion,
+					Kind:    mocks.CRDKind,
+				},
+				ResourceRef: otherNsResourceRef,
+				Namespace:   &otherNs,
+			},
+		}
+
 		serviceCtxs, err := buildServiceContexts(
-			f.FakeDynClient(), ns, extractServiceSelectors(sbr), false, restMapper)
-		require.NoError(t, err)
-		require.NotEmpty(t, serviceCtxs)
+			logger, f.FakeDynClient(), sameNs, extractServiceSelectors(sbr), false, restMapper)
+
+		require.NoError(t, err, "buildServiceContexts must execute without errors")
+		require.Len(t, serviceCtxs, 2, "buildServiceContexts must return both service contexts")
+
+		{
+			otherNsCtx := serviceCtxs[0]
+			expectedKeys := []string{"status", "dbCredentials"}
+			expectedDbCredentials := map[string]interface{}{
+				"username": "other-ns-username",
+				"password": "other-ns-password",
+			}
+			gotDbCredentials, ok, err :=
+				unstructured.NestedFieldCopy(otherNsCtx.service.Object, expectedKeys...)
+			require.NoError(t, err)
+			require.True(t, ok)
+			require.Equal(t, expectedDbCredentials, gotDbCredentials)
+		}
+
+		{
+			sameNsCtx := serviceCtxs[1]
+			expectedKeys := []string{"status", "dbCredentials"}
+			expectedDbCredentials := map[string]interface{}{
+				"username": "same-ns-username",
+				"password": "same-ns-password",
+			}
+			gotDbCredentials, ok, err :=
+				unstructured.NestedFieldCopy(sameNsCtx.service.Object, expectedKeys...)
+			require.NoError(t, err)
+			require.True(t, ok)
+			require.Equal(t, expectedDbCredentials, gotDbCredentials)
+		}
 	})
 }
 
@@ -120,7 +208,7 @@ func TestFindOwnedResourcesCtxs_ConfigMap(t *testing.T) {
 		expected := map[string]interface{}{
 			"": map[string]interface{}{
 				"password": "password",
-				"user":     "user",
+				"username": "user",
 			},
 		}
 		require.Equal(t, expected, got[0].envVars)
@@ -167,7 +255,7 @@ func TestFindOwnedResourcesCtxs_Secrets(t *testing.T) {
 			restMapper := testutils.BuildTestRESTMapper()
 
 			for _, secret := range tC.secrets {
-				secret := mocks.SecretMock("test", secret)
+				secret := mocks.SecretMock("test", secret, nil)
 				us := &unstructured.Unstructured{}
 				uc, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&secret)
 				require.NoError(t, err)
diff --git a/test/acceptance/README.md b/test/acceptance/README.md
new file mode 100644
index 00000000..0d4eaf0f
--- /dev/null
+++ b/test/acceptance/README.md
@@ -0,0 +1,10 @@
+# How to execute the script
+
+## Install python dependencies
+pip install -r test/acceptance/features/requirements.txt
+
+## Use this make target to check the test/acceptance python behave framework is a clean code
+make lint-python-code
+
+## Use this command to run the tests
+make test-acceptance
\ No newline at end of file
diff --git a/test/acceptance/features/bindToService.feature b/test/acceptance/features/bindToService.feature
new file mode 100644
index 00000000..cff3df40
--- /dev/null
+++ b/test/acceptance/features/bindToService.feature
@@ -0,0 +1,218 @@
+Feature: Bind an application to a service
+
+    As a user of Service Binding Operator
+    I want to bind applications to services it depends on
+
+    Background:
+        Given Namespace [TEST_NAMESPACE] is used
+        * Service Binding Operator is running
+        * PostgreSQL DB operator is installed
+
+    Scenario: Bind an imported Node.js application to PostgreSQL database in the following order: Application, DB and Service Binding Request
+        Given Imported Nodejs application "nodejs-rest-http-crud-a-d-s" is running
+        * DB "db-demo-a-d-s" is running
+        When Service Binding Request is applied to connect the database and the application
+            """
+            apiVersion: apps.openshift.io/v1alpha1
+            kind: ServiceBindingRequest
+            metadata:
+                name: binding-request-a-d-s
+            spec:
+                applicationSelector:
+                    resourceRef: nodejs-rest-http-crud-a-d-s
+                    group: apps
+                    version: v1
+                    resource: deployments
+                backingServiceSelector:
+                    group: postgresql.baiju.dev
+                    version: v1alpha1
+                    kind: Database
+                    resourceRef: db-demo-a-d-s
+            """
+        Then application should be re-deployed
+        And application should be connected to the DB "db-demo-a-d-s"
+        And jq ".status.conditions[] | select(.type=="CollectionReady").status" of Service Binding Request "binding-request-a-d-s" should be changed to "True"
+        And jq ".status.conditions[] | select(.type=="InjectionReady").status" of Service Binding Request "binding-request-a-d-s" should be changed to "True"
+
+    Scenario: Bind an imported Node.js application to PostgreSQL database in the following order: Application, Service Binding Request and DB
+        Given Imported Nodejs application "nodejs-rest-http-crud-a-s-d" is running
+        * Service Binding Request is applied to connect the database and the application
+            """
+            apiVersion: apps.openshift.io/v1alpha1
+            kind: ServiceBindingRequest
+            metadata:
+                name: binding-request-a-s-d
+            spec:
+                applicationSelector:
+                    resourceRef: nodejs-rest-http-crud-a-s-d
+                    group: apps
+                    version: v1
+                    resource: deployments
+                backingServiceSelector:
+                    group: postgresql.baiju.dev
+                    version: v1alpha1
+                    kind: Database
+                    resourceRef: db-demo-a-s-d
+            """
+        When DB "db-demo-a-s-d" is running
+        Then application should be re-deployed
+        And application should be connected to the DB "db-demo-a-s-d"
+        And jq ".status.conditions[] | select(.type=="CollectionReady").status" of Service Binding Request "binding-request-a-s-d" should be changed to "True"
+        And jq ".status.conditions[] | select(.type=="InjectionReady").status" of Service Binding Request "binding-request-a-s-d" should be changed to "True"
+
+    Scenario: Bind an imported Node.js application to PostgreSQL database in the following order: DB, Application and Service Binding Request
+        Given DB "db-demo-d-a-s" is running
+        * Imported Nodejs application "nodejs-rest-http-crud-d-a-s" is running
+        When Service Binding Request is applied to connect the database and the application
+            """
+            apiVersion: apps.openshift.io/v1alpha1
+            kind: ServiceBindingRequest
+            metadata:
+                name: binding-request-d-a-s
+            spec:
+                applicationSelector:
+                    resourceRef: nodejs-rest-http-crud-d-a-s
+                    group: apps
+                    version: v1
+                    resource: deployments
+                backingServiceSelector:
+                    group: postgresql.baiju.dev
+                    version: v1alpha1
+                    kind: Database
+                    resourceRef: db-demo-d-a-s
+            """
+        Then application should be re-deployed
+        And application should be connected to the DB "db-demo-d-a-s"
+        And jq ".status.conditions[] | select(.type=="CollectionReady").status" of Service Binding Request "binding-request-d-a-s" should be changed to "True"
+        And jq ".status.conditions[] | select(.type=="InjectionReady").status" of Service Binding Request "binding-request-d-a-s" should be changed to "True"
+
+    # Currently disabled as not supported by SBO
+    @disabled
+    Scenario: Bind an imported Node.js application to PostgreSQL database in the following order: DB, Service Binding Request and Application
+        Given DB "db-demo-d-s-a" is running
+        * Service Binding Request is applied to connect the database and the application
+            """
+            apiVersion: apps.openshift.io/v1alpha1
+            kind: ServiceBindingRequest
+            metadata:
+                name: binding-request-d-s-a
+            spec:
+                applicationSelector:
+                    resourceRef: nodejs-rest-http-crud-d-s-a
+                    group: apps
+                    version: v1
+                    resource: deployments
+                backingServiceSelector:
+                    group: postgresql.baiju.dev
+                    version: v1alpha1
+                    kind: Database
+                    resourceRef: db-demo-d-s-a
+            """
+        When Imported Nodejs application "nodejs-rest-http-crud-d-s-a" is running
+        Then application should be re-deployed
+        And application should be connected to the DB "db-demo-d-s-a"
+        And jq ".status.conditions[] | select(.type=="CollectionReady").status" of Service Binding Request "binding-request-d-s-a" should be changed to "True"
+        And jq ".status.conditions[] | select(.type=="InjectionReady").status" of Service Binding Request "binding-request-d-s-a" should be changed to "True"
+
+    # Currently disabled as not supported by SBO
+    @disabled
+    Scenario: Bind an imported Node.js application to PostgreSQL database in the following order: Service Binding Request, Application and DB
+        Given Service Binding Request is applied to connect the database and the application
+            """
+            apiVersion: apps.openshift.io/v1alpha1
+            kind: ServiceBindingRequest
+            metadata:
+                name: binding-request-s-a-d
+            spec:
+                applicationSelector:
+                    resourceRef: nodejs-rest-http-crud-s-a-d
+                    group: apps
+                    version: v1
+                    resource: deployments
+                backingServiceSelector:
+                    group: postgresql.baiju.dev
+                    version: v1alpha1
+                    kind: Database
+                    resourceRef: db-demo-s-a-d
+            """
+        * Imported Nodejs application "nodejs-rest-http-crud-s-a-d" is running
+        When DB "db-demo-s-a-d" is running
+        Then application should be re-deployed
+        And application should be connected to the DB "db-demo-s-a-d"
+        And jq ".status.conditions[] | select(.type=="CollectionReady").status" of Service Binding Request "binding-request-s-a-d" should be changed to "True"
+        And jq ".status.conditions[] | select(.type=="InjectionReady").status" of Service Binding Request "binding-request-s-a-d" should be changed to "True"
+
+    # Currently disabled as not supported by SBO
+    @disabled
+    Scenario: Bind an imported Node.js application to PostgreSQL database in the following order: Service Binding Request, DB and Application
+        Given Service Binding Request is applied to connect the database and the application
+            """
+            apiVersion: apps.openshift.io/v1alpha1
+            kind: ServiceBindingRequest
+            metadata:
+                name: binding-request-s-d-a
+            spec:
+                applicationSelector:
+                    resourceRef: nodejs-rest-http-crud-s-d-a
+                    group: apps
+                    version: v1
+                    resource: deployments
+                backingServiceSelector:
+                    group: postgresql.baiju.dev
+                    version: v1alpha1
+                    kind: Database
+                    resourceRef: db-demo-s-d-a
+            """
+        * DB "db-demo-s-d-a" is running
+        When Imported Nodejs application "nodejs-rest-http-crud-s-d-a" is running
+        Then application should be re-deployed
+        And application should be connected to the DB "db-demo-s-d-a"
+        And jq ".status.conditions[] | select(.type=="CollectionReady").status" of Service Binding Request "binding-request-s-d-a" should be changed to "True"
+        And jq ".status.conditions[] | select(.type=="InjectionReady").status" of Service Binding Request "binding-request-s-d-a" should be changed to "True"
+
+    @negative
+    Scenario: Attempt to bind a non existing application to PostgreSQL database
+        Given DB "db-demo-missing-app" is running
+        * Imported Nodejs application "nodejs-missing-app" is not running
+        When Service Binding Request is applied to connect the database and the application
+            """
+            apiVersion: apps.openshift.io/v1alpha1
+            kind: ServiceBindingRequest
+            metadata:
+                name: binding-request-missing-app
+            spec:
+                applicationSelector:
+                    resourceRef: nodejs-missing-app
+                    group: apps
+                    version: v1
+                    resource: deployments
+                backingServiceSelector:
+                    group: postgresql.baiju.dev
+                    version: v1alpha1
+                    kind: Database
+                    resourceRef: db-demo-missing-app
+            """
+        Then jq ".status.conditions[] | select(.type=="CollectionReady").status" of Service Binding Request "binding-request-missing-app" should be changed to "True"
+        And jq ".status.conditions[] | select(.type=="InjectionReady").status" of Service Binding Request "binding-request-missing-app" should be changed to "False"
+        And jq ".status.conditions[] | select(.type=="InjectionReady").reason" of Service Binding Request "binding-request-missing-app" should be changed to "ApplicationNotFound"
+
+    @negative
+    Scenario: Service Binding Request without application selector
+        Given Imported Nodejs application "nodejs-empty-app" is running
+        And DB "db-demo-empty-app" is running
+        When Service Binding Request is applied to connect the database and the application
+            """
+            apiVersion: apps.openshift.io/v1alpha1
+            kind: ServiceBindingRequest
+            metadata:
+                name: binding-request-empty-app
+            spec:
+                backingServiceSelector:
+                    group: postgresql.baiju.dev
+                    version: v1alpha1
+                    kind: Database
+                    resourceRef: db-demo-empty-app
+            """
+        Then jq ".status.conditions[] | select(.type=="CollectionReady").status" of Service Binding Request "binding-request-empty-app" should be changed to "True"
+        And jq ".status.conditions[] | select(.type=="InjectionReady").status" of Service Binding Request "binding-request-empty-app" should be changed to "False"
+        And jq ".status.conditions[] | select(.type=="InjectionReady").reason" of Service Binding Request "binding-request-empty-app" should be changed to "EmptyApplicationSelector"
diff --git a/test/acceptance/features/environment.py b/test/acceptance/features/environment.py
new file mode 100644
index 00000000..19d40560
--- /dev/null
+++ b/test/acceptance/features/environment.py
@@ -0,0 +1,25 @@
+"""
+before_step(context, step), after_step(context, step)
+    These run before and after every step.
+    The step passed in is an instance of Step.
+before_scenario(context, scenario), after_scenario(context, scenario)
+    These run before and after each scenario is run.
+    The scenario passed in is an instance of Scenario.
+before_feature(context, feature), after_feature(context, feature)
+    These run before and after each feature file is exercised.
+    The feature passed in is an instance of Feature.
+before_all(context), after_all(context)
+    These run before and after the whole shooting match.
+"""
+
+import subprocess
+from pyshould import should
+
+
+def before_scenario(_context, _scenario):
+    print("Getting OC status before {} scenario".format(_scenario))
+    code, output = subprocess.getstatusoutput('oc get project default')
+    print("[CODE] {}".format(code))
+    print("[CMD] {}".format(output))
+    code | should.be_equal_to(0)
+    print("***Connected to cluster***")
diff --git a/test/acceptance/features/requirements.txt b/test/acceptance/features/requirements.txt
new file mode 100644
index 00000000..200dd264
--- /dev/null
+++ b/test/acceptance/features/requirements.txt
@@ -0,0 +1,3 @@
+behave
+pyshould
+requests
\ No newline at end of file
diff --git a/test/acceptance/features/steps/command.py b/test/acceptance/features/steps/command.py
new file mode 100644
index 00000000..3af3f439
--- /dev/null
+++ b/test/acceptance/features/steps/command.py
@@ -0,0 +1,48 @@
+import subprocess
+import time
+import os
+
+
+class Command(object):
+    path = ""
+    env = {}
+
+    def __init__(self, path=None):
+        if path is None:
+            self.path = os.getcwd()
+        else:
+            self.path = path
+
+        self.setenv("KUBECONFIG", os.getenv("KUBECONFIG"))
+        self.setenv("PATH", os.getenv("PATH"))
+
+    def setenv(self, key, value):
+        self.env[key] = value
+
+    def run(self, cmd, stdin=None):
+        output = None
+        exit_code = 0
+        try:
+            if stdin is None:
+                output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, cwd=self.path, env=self.env)
+            else:
+                output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, cwd=self.path, env=self.env, input=stdin.encode("utf-8"))
+        except subprocess.CalledProcessError as err:
+            output = err.output
+            exit_code = err.returncode
+            print('ERROR MESSGE:', output)
+            print('ERROR CODE:', exit_code)
+        return output.decode("utf-8"), exit_code
+
+    def run_wait_for_status(self, cmd, status=None, interval=10, timeout=60):
+        start = 0
+        if status is not None:
+            while ((start + interval) <= timeout):
+                cmd_output, exit_code = self.run(cmd)
+                if status in cmd_output:
+                    return True, cmd_output, exit_code
+                time.sleep(interval)
+                start += interval
+
+        print("ERROR: Time out while waiting for status message.")
+        return False, self.run(cmd)
diff --git a/test/acceptance/features/steps/dboperator.py b/test/acceptance/features/steps/dboperator.py
new file mode 100644
index 00000000..01187d3e
--- /dev/null
+++ b/test/acceptance/features/steps/dboperator.py
@@ -0,0 +1,55 @@
+import re
+from pyshould import should, should_not
+
+from command import Command
+from openshift import Openshift
+
+
+class DbOperator():
+
+    openshift = Openshift()
+    cmd = Command()
+
+    pod_name_pattern = "{name}.*"
+
+    name = ""
+    namespace = ""
+    operator_source_name = "db-operators"
+    operator_registry_namespace = "pmacik"
+    operator_registry_channel = "stable"
+    package_name = "db-operators"
+
+    def __init__(self, name="postgresql-operator", namespace="openshift-operators"):
+        self.name = name
+        self.namespace = namespace
+
+    def is_running(self, wait=False):
+        if wait:
+            pod_name = self.openshift.wait_for_pod(self.pod_name_pattern.format(name=self.name), self.namespace)
+        else:
+            pod_name = self.openshift.search_pod_in_namespace(self.pod_name_pattern.format(name=self.name), self.namespace)
+        if pod_name is not None:
+            operator_pod_status = self.openshift.check_pod_status(pod_name, self.namespace)
+            print("The pod {} is running: {}".format(self.name, operator_pod_status))
+            return operator_pod_status
+        else:
+            return False
+
+    def install_operator_source(self):
+        install_src_output = self.openshift.create_operator_source(self.operator_source_name, self.operator_registry_namespace)
+        if not re.search(r'.*operatorsource.operators.coreos.com/%s\s(unchanged|created)' % self.operator_source_name, install_src_output):
+            print("Failed to create {} operator source".format(self.operator_source_name))
+            return False
+        return self.openshift.wait_for_package_manifest(self.package_name, self.operator_source_name, self.operator_registry_channel)
+
+    def install_operator_subscription(self):
+        install_sub_output = self.openshift.create_operator_subscription(self.package_name, self.operator_source_name, self.operator_registry_channel)
+        return re.search(r'.*subscription.operators.coreos.com/%s\s(unchanged|created)' % self.operator_source_name, install_sub_output)
+
+    def get_package_manifest(self):
+        cmd = 'oc get packagemanifest %s -o "jsonpath={.metadata.name}"' % self.pkgManifest
+        manifest = self.cmd.run_check_for_status(
+            cmd, status=self.pkgManifest)
+        manifest | should_not.be_equal_to(None)
+        manifest | should.equal(self.pkgManifest)
+        return manifest
diff --git a/test/acceptance/features/steps/namespace.py b/test/acceptance/features/steps/namespace.py
new file mode 100644
index 00000000..ec2e3905
--- /dev/null
+++ b/test/acceptance/features/steps/namespace.py
@@ -0,0 +1,33 @@
+import re
+
+from command import Command
+
+
+class Namespace():
+    def __init__(self, name):
+        self.name = name
+        self.cmd = Command()
+
+    def create(self):
+        create_namespace_output, exit_code = self.cmd.run("oc new-project {}".format(self.name))
+        if re.search(r'Now using project \"%s\"\son\sserver' % self.name, create_namespace_output) or \
+                re.search(r'.*Already\son\sproject\s\"%s\"\son\sserver.*' % self.name, create_namespace_output):
+            return True
+        elif re.search(r'.*project.project.openshift.io\s\"%s\"\salready exists' % self.name, create_namespace_output):
+            return self.switch_to()
+        else:
+            print("Returned a different value {}".format(create_namespace_output))
+        return False
+
+    def is_present(self):
+        output, exit_code = self.cmd.run('oc get ns {}'.format(self.name))
+        return exit_code == 0
+
+    def switch_to(self):
+        create_namespace_output, exit_code = self.cmd.run('oc project {}'.format(self.name))
+        if re.search(r'Now using project \"%s\"\son\sserver' % self.name, create_namespace_output):
+            return True
+        elif re.search(r'.*Already\son\sproject\s\"%s\"\son\sserver.*' % self.name, create_namespace_output):
+            return True
+        else:
+            return False
diff --git a/test/acceptance/features/steps/nodejs_application.py b/test/acceptance/features/steps/nodejs_application.py
new file mode 100644
index 00000000..779ee980
--- /dev/null
+++ b/test/acceptance/features/steps/nodejs_application.py
@@ -0,0 +1,105 @@
+from openshift import Openshift
+from command import Command
+import re
+import requests
+import time
+
+
+class NodeJSApp(object):
+
+    nodejs_app = "https://github.com/pmacik/nodejs-rest-http-crud"
+    api_end_point = 'http://{route_url}/api/status/dbNameCM'
+    openshift = Openshift()
+
+    pod_name_pattern = "{name}.*$(?<!-build)"
+
+    name = ""
+    namespace = ""
+
+    def __init__(self, name, namespace):
+        self.cmd = Command()
+        self.name = name
+        self.namespace = namespace
+
+    def is_running(self, wait=False):
+        deployment_flag = False
+
+        if wait:
+            pod_name = self.openshift.wait_for_pod(self.get_pod_name_pattern(), self.namespace, timeout=180)
+        else:
+            pod_name = self.openshift.search_pod_in_namespace(self.get_pod_name_pattern(), self.namespace)
+
+        if pod_name is not None:
+            application_pod_status = self.openshift.check_pod_status(pod_name, self.namespace, wait_for_status="Running")
+            print("The pod {} is running: {}".format(pod_name, application_pod_status))
+
+            deployment = self.openshift.search_resource_in_namespace("deployments", f"{self.name}.*", self.namespace)
+            if deployment is not None:
+                print("deployment is {}".format(deployment))
+                deployment_flag = True
+
+            if application_pod_status and deployment_flag:
+                return True
+            else:
+                return False
+        else:
+            return False
+
+    def install(self):
+        nodejs_app_arg = "nodejs~" + self.nodejs_app
+        cmd = f"oc new-app {nodejs_app_arg} --name {self.name} -n {self.namespace}"
+        (create_new_app_output, exit_code) = self.cmd.run(cmd)
+        if exit_code != 0:
+            return False
+        for pattern in [f'imagestream.image.openshift.io\\s\"{self.name}\"\\screated',
+                        f'deployment.apps\\s\"{self.name}\"\\screated',
+                        f'service\\s\"{self.name}\"\\screated']:
+            if not re.search(pattern, create_new_app_output):
+                return False
+        if not self.openshift.expose_service_route(self.name, self.namespace):
+            print("Unable to expose the service with build config")
+            return False
+        return True
+
+    def get_db_name_from_api(self, interval=5, timeout=20):
+        route_url = self.openshift.get_route_host(self.name, self.namespace)
+        if route_url is None:
+            return None
+        start = 0
+        while ((start + interval) <= timeout):
+            db_name = requests.get(url=self.api_end_point.format(route_url=route_url))
+            if db_name.status_code == 200:
+                return db_name.text
+            time.sleep(interval)
+            start += interval
+        return None
+
+    def get_observed_generation(self):
+        return self.openshift.get_resource_info_by_jsonpath("deployment", self.name, self.namespace, "{.status.observedGeneration}")
+
+    def get_running_pod_name(self, interval=5, timeout=60):
+        start = 0
+        while ((start + interval) <= timeout):
+            pod_list = self.openshift.get_pod_lst(self.namespace)
+            for pod in pod_list.split(" "):
+                if re.fullmatch(self.get_pod_name_pattern(), pod):
+                    if self.openshift.get_pod_status(pod, self.namespace) == "Running":
+                        return pod
+            time.sleep(interval)
+            start += interval
+        return None
+
+    def get_redeployed_pod_name(self, old_pod_name, interval=5, timeout=60):
+        start = 0
+        while ((start + interval) <= timeout):
+            pod_list = self.openshift.get_pod_lst(self.namespace)
+            for pod in pod_list.split(" "):
+                if pod != old_pod_name and re.fullmatch(self.get_pod_name_pattern(), pod):
+                    if self.openshift.get_pod_status(pod, self.namespace) == "Running":
+                        return pod
+            time.sleep(interval)
+            start += interval
+        return None
+
+    def get_pod_name_pattern(self):
+        return self.pod_name_pattern.format(name=self.name)
diff --git a/test/acceptance/features/steps/openshift.py b/test/acceptance/features/steps/openshift.py
new file mode 100644
index 00000000..10bafe93
--- /dev/null
+++ b/test/acceptance/features/steps/openshift.py
@@ -0,0 +1,190 @@
+import re
+import time
+from pyshould import should
+from command import Command
+
+nodejs_app = "https://github.com/pmacik/nodejs-rest-http-crud"
+
+
+class Openshift(object):
+    def __init__(self):
+        self.cmd = Command()
+        self.operator_source_yaml_template = '''
+---
+apiVersion: operators.coreos.com/v1
+kind: OperatorSource
+metadata:
+    name: {name}
+    namespace: openshift-marketplace
+spec:
+    type: appregistry
+    endpoint: https://quay.io/cnr
+    registryNamespace: {registry_namespace}
+'''
+        self.operator_subscription_yaml_template = '''
+---
+apiVersion: operators.coreos.com/v1alpha1
+kind: Subscription
+metadata:
+    name: '{name}'
+    namespace: openshift-operators
+spec:
+    channel: '{channel}' # the quotes are necessary to avoid conversions from strings like '1.0' to be converted to actual decimal numbers
+    installPlanApproval: Automatic
+    name: '{name}'
+    source: '{operator_source_name}'
+    sourceNamespace: openshift-marketplace
+    startingCSV: '{csv_version}'
+'''
+
+    def get_pod_lst(self, namespace):
+        return self.get_resource_lst("pods", namespace)
+
+    def get_resource_lst(self, resource_plural, namespace):
+        (output, exit_code) = self.cmd.run(f'oc get {resource_plural} -n {namespace} -o "jsonpath={{.items[*].metadata.name}}"')
+        exit_code | should.be_equal_to(0)
+        return output
+
+    def search_item_in_lst(self, lst, search_pattern):
+        lst_arr = lst.split(" ")
+        for item in lst_arr:
+            if re.fullmatch(search_pattern, item) is not None:
+                print(f"item matched {item}")
+                return item
+        print("Given item not matched from the list of pods")
+        return None
+
+    def search_pod_in_namespace(self, pod_name_pattern, namespace):
+        return self.search_resource_in_namespace("pods", pod_name_pattern, namespace)
+
+    def search_resource_in_namespace(self, resource_plural, name_pattern, namespace):
+        print(f"Searching for {resource_plural} that matches {name_pattern} in {namespace} namespace")
+        lst = self.get_resource_lst(resource_plural, namespace)
+        if len(lst) != 0:
+            print("Resource list is {}".format(lst))
+            return self.search_item_in_lst(lst, name_pattern)
+        else:
+            print('Resource list is empty under namespace - {}'.format(namespace))
+            return None
+
+    def is_resource_in(self, resource_type):
+        output, exit_code = self.cmd.run(f'oc get {resource_type}')
+        return exit_code == 0
+
+    def wait_for_pod(self, pod_name_pattern, namespace, interval=5, timeout=60):
+        pod = self.search_pod_in_namespace(pod_name_pattern, namespace)
+        start = 0
+        if pod is not None:
+            return pod
+        else:
+            while ((start + interval) <= timeout):
+                pod = self.search_pod_in_namespace(pod_name_pattern, namespace)
+                if pod is not None:
+                    return pod
+                time.sleep(interval)
+                start += interval
+        return None
+
+    def check_pod_status(self, pod_name, namespace, wait_for_status="Running"):
+        cmd = f'oc get pod {pod_name} -n {namespace} -o "jsonpath={{.status.phase}}"'
+        status_found, output, exit_status = self.cmd.run_wait_for_status(cmd, wait_for_status)
+        return status_found
+
+    def get_pod_status(self, pod_name, namespace):
+        cmd = f'oc get pod {pod_name} -n {namespace} -o "jsonpath={{.status.phase}}"'
+        output, exit_status = self.cmd.run(cmd)
+        print(f"Get pod status: {output}, {exit_status}")
+        if exit_status == 0:
+            return output
+        return None
+
+    def oc_apply(self, yaml):
+        (output, exit_code) = self.cmd.run("oc apply -f -", yaml)
+        return output
+
+    def create_operator_source(self, name, registry_namespace):
+        operator_source = self.operator_source_yaml_template.format(name=name, registry_namespace=registry_namespace)
+        return self.oc_apply(operator_source)
+
+    def get_current_csv(self, package_name, catalog, channel):
+        cmd = f'oc get packagemanifests -o json | jq -r \'.items[] \
+            | select(.metadata.name=="{package_name}") \
+            | select(.status.catalogSource=="{catalog}").status.channels[] \
+            | select(.name=="{channel}").currentCSV\''
+        current_csv, exit_code = self.cmd.run(cmd)
+
+        if current_csv is None:
+            return current_csv
+
+        current_csv = current_csv.strip("\n")
+        if current_csv == "" or exit_code != 0:
+            current_csv = None
+        return current_csv
+
+    def create_operator_subscription(self, package_name, operator_source_name, channel):
+        operator_subscription = self.operator_subscription_yaml_template.format(
+            name=package_name, operator_source_name=operator_source_name,
+            channel=channel, csv_version=self.get_current_csv(package_name, operator_source_name, channel))
+        return self.oc_apply(operator_subscription)
+
+    def wait_for_package_manifest(self, package_name, operator_source_name, operator_channel, interval=5, timeout=120):
+        current_csv = self.get_current_csv(package_name, operator_source_name, operator_channel)
+        start = 0
+        if current_csv is not None:
+            return True
+        else:
+            while ((start + interval) <= timeout):
+                current_csv = self.get_current_csv(package_name, operator_source_name, operator_channel)
+                if current_csv is not None:
+                    return True
+                time.sleep(interval)
+                start += interval
+        return False
+
+    def expose_service_route(self, service_name, namespace):
+        output, exit_code = self.cmd.run(f'oc expose svc/{service_name} -n {namespace} --name={service_name}')
+        return re.search(r'.*%s\sexposed' % service_name, output)
+
+    def get_route_host(self, name, namespace):
+        (output, exit_code) = self.cmd.run(f'oc get route {name} -n {namespace} -o "jsonpath={{.status.ingress[0].host}}"')
+        exit_code | should.be_equal_to(0)
+        return output
+
+    def check_for_deployment_status(self, deployment_name, namespace, wait_for_status="True"):
+        deployment_status_cmd = f'oc get deployment {deployment_name} -n {namespace} -o "jsonpath={{.status.conditions[*].status}}"'
+        deployment_status, exit_code = self.cmd.run_wait_for_status(deployment_status_cmd, wait_for_status, 5, 300)
+        exit_code | should.be_equal_to(0)
+        return deployment_status
+
+    def get_deployment_env_info(self, name, namespace):
+        env_cmd = f'oc get deploy {name} -n {namespace} "jsonpath={{.spec.template.spec.containers[0].env}}"'
+        env, exit_code = self.cmd.run(env_cmd)
+        exit_code | should.be_equal_to(0)
+        env_from_cmd = f'oc get deploy {name} -n {namespace} "jsonpath={{.spec.template.spec.containers[0].envFrom}}"'
+        env_from, exit_code = self.cmd.run(env_from_cmd)
+        exit_code | should.be_equal_to(0)
+        return env, env_from
+
+    def get_resource_info_by_jsonpath(self, resource_type, name, namespace, json_path, wait=False):
+        output, exit_code = self.cmd.run(f'oc get {resource_type} {name} -n {namespace} -o "jsonpath={json_path}"')
+        if exit_code != 0:
+            if wait:
+                attempts = 5
+                while exit_code != 0 and attempts > 0:
+                    output, exit_code = self.cmd.run(f'oc get {resource_type} {name} -n {namespace} -o "jsonpath={json_path}"')
+                    attempts -= 1
+                    time.sleep(5)
+        exit_code | should.be_equal_to(0).desc(f'Exit code should be 0:\n OUTPUT:\n{output}')
+        return output
+
+    def get_resource_info_by_jq(self, resource_type, name, namespace, jq_expression, wait=False):
+        output, exit_code = self.cmd.run(f'oc get {resource_type} {name} -n {namespace} -o json | jq -rc \'{jq_expression}\'')
+        if exit_code != 0:
+            if wait:
+                attempts = 5
+                while exit_code != 0 and attempts > 0:
+                    output, exit_code = self.cmd.run(f'oc get {resource_type} {name} -n {namespace} -o json | jq -rc \'{jq_expression}\'')
+                    attempts -= 1
+                    time.sleep(5)
+        exit_code | should.be_equal_to(0).desc(f'Exit code should be 0:\n OUTPUT:\n{output}')
+        return output.rstrip("\n")
diff --git a/test/acceptance/features/steps/postgres_db.py b/test/acceptance/features/steps/postgres_db.py
new file mode 100644
index 00000000..37118953
--- /dev/null
+++ b/test/acceptance/features/steps/postgres_db.py
@@ -0,0 +1,60 @@
+import re
+
+from command import Command
+from openshift import Openshift
+
+
+class PostgresDB(object):
+
+    cmd = Command()
+    openshift = Openshift()
+
+    pod_name_pattern = "{name}.*"
+
+    name = ""
+    namespace = ""
+
+    database_yaml_template = """---
+apiVersion: postgresql.baiju.dev/v1alpha1
+kind: Database
+metadata:
+  name: {name}
+  namespace: {namespace}
+spec:
+  image: docker.io/postgres
+  imageName: postgres
+  dbName: {name}
+"""
+
+    def __init__(self, name, namespace):
+        self.name = name
+        self.namespace = namespace
+
+    def create(self):
+        db_create_output = self.openshift.oc_apply(self.database_yaml_template.format(name=self.name, namespace=self.namespace))
+        return re.search(r'.*database.postgresql.baiju.dev/%s\s(created|unchanged)' % self.name, db_create_output)
+
+    def is_running(self, wait=False):
+        if wait:
+            pod_name = self.openshift.wait_for_pod(self.pod_name_pattern.format(name=self.name), self.namespace, timeout=120)
+        else:
+            pod_name = self.openshift.search_pod_in_namespace(self.pod_name_pattern.format(name=self.name), self.namespace)
+        if pod_name is not None:
+            pod_status = self.openshift.check_pod_status(pod_name, self.namespace)
+            print("The pod {} is running: {}".format(self.name, pod_status))
+            output, exit_code = self.cmd.run(f'oc get db {self.name} -n {self.namespace} -o jsonpath="{{.status.dbConnectionIP}}"')
+            if exit_code == 0 and re.search(r'\d+\.\d+\.\d+\.\d+', output):
+                print(f"The DB {self.name} is up and listening at {output}.")
+                return True
+        return False
+
+    def get_db_instance_name(self, project):
+        cmd = 'oc get db -n %s -o "jsonpath={.items[*].metadata.name}"' % project
+        return self.cmd.run(cmd)
+
+    def get_connection_ip(self):
+        cmd = 'oc get db %s -o "jsonpath={.status.dbConnectionIP}"' % self.db_instance
+        return self.cmd.run(cmd)
+
+    def check_pod_status(self, status="Running"):
+        return self.openshift.check_pod_status(self.name, self.namespace, wait_for_status=status)
diff --git a/test/acceptance/features/steps/service_binding_request.py b/test/acceptance/features/steps/service_binding_request.py
new file mode 100644
index 00000000..4cb32f2a
--- /dev/null
+++ b/test/acceptance/features/steps/service_binding_request.py
@@ -0,0 +1,12 @@
+import re
+
+from openshift import Openshift
+
+
+class ServiceBindingRequest(object):
+
+    openshift = Openshift()
+
+    def create(self, yaml):
+        sbr_create_output = self.openshift.oc_apply(yaml)
+        return re.search(r'.*servicebindingrequest.apps.openshift.io/.*(created|unchanged)', sbr_create_output)
diff --git a/test/acceptance/features/steps/servicebindingoperator.py b/test/acceptance/features/steps/servicebindingoperator.py
new file mode 100644
index 00000000..85fe2515
--- /dev/null
+++ b/test/acceptance/features/steps/servicebindingoperator.py
@@ -0,0 +1,33 @@
+import os
+
+from openshift import Openshift
+from pyshould import should, should_not
+
+
+class Servicebindingoperator():
+    openshift = Openshift()
+    name = ""
+    namespace = ""
+
+    def __init__(self,  name="service-binding-operator", namespace="openshift-operators"):
+        self.namespace = namespace
+        self.name = name
+
+    def check_resources(self):
+        self.openshift.is_resource_in("servicebindingrequest") | should.be_truthy.desc("CRD is in")
+        self.openshift.search_resource_in_namespace("rolebindings", self.name, self.namespace) | should_not.be_none.desc("Role binding is in")
+        self.openshift.search_resource_in_namespace("roles", self.name, self.namespace) | should_not.be_none.desc("Role is in")
+        self.openshift.search_resource_in_namespace("serviceaccounts", self.name, self.namespace) | should_not.be_none.desc("Service Account")
+        return True
+
+    def is_running(self):
+        start_sbo = os.getenv("TEST_ACCEPTANCE_START_SBO")
+        start_sbo | should_not.be_none.desc("TEST_ACCEPTANCE_START_SBO is set")
+        start_sbo | should.be_in({"local", "operator-hub"})
+
+        if start_sbo == "local":
+            os.getenv("TEST_ACCEPTANCE_SBO_STARTED") | should_not.start_with("FAILED").desc("TEST_ACCEPTANCE_SBO_STARTED is not FAILED")
+        elif start_sbo == "operator-hub":
+            return False
+
+        return self.check_resources()
diff --git a/test/acceptance/features/steps/steps.py b/test/acceptance/features/steps/steps.py
new file mode 100644
index 00000000..ec881eb2
--- /dev/null
+++ b/test/acceptance/features/steps/steps.py
@@ -0,0 +1,188 @@
+# @mark.steps
+# ----------------------------------------------------------------------------
+# STEPS:
+# ----------------------------------------------------------------------------
+import os
+import re
+
+from behave import given, then, when
+from pyshould import should, should_not
+
+from servicebindingoperator import Servicebindingoperator
+from dboperator import DbOperator
+from openshift import Openshift
+from postgres_db import PostgresDB
+from namespace import Namespace
+from nodejs_application import NodeJSApp
+from service_binding_request import ServiceBindingRequest
+
+
+# STEP
+@given(u'Namespace "{namespace_name}" is used')
+def given_namespace_is_used(context, namespace_name):
+    namespace = Namespace(namespace_name)
+    if not namespace.is_present():
+        print("Namespace is not present, creating namespace: {}...".format(namespace_name))
+        namespace.create() | should.be_truthy.desc("Namespace {} is created".format(namespace_name))
+    print("Namespace {} is created!!!".format(namespace_name))
+    context.namespace = namespace
+
+
+# STEP
+@given(u'Namespace [{namespace_env}] is used')
+def given_namespace_from_env_is_used(context, namespace_env):
+    env = os.getenv(namespace_env, "")
+    env | should_not.be_none.desc(f"{namespace_env} env variable is set")
+    print(f"{namespace_env} = {env}")
+    given_namespace_is_used(context, env)
+
+
+# STEP
+sbo_is_running_in_namespace_step = u'Service Binding Operator is running in "{operator_namespace}" namespace'
+
+
+@given(sbo_is_running_in_namespace_step)
+@when(sbo_is_running_in_namespace_step)
+def sbo_is_running_in_namespace(context, operator_namespace):
+    """
+    Checks if the SBO is up and running in the given namespace
+    """
+    sb_operator = Servicebindingoperator(namespace=operator_namespace)
+    sb_operator.is_running() | should.be_truthy.desc("Service Binding Operator is running")
+    print("Service binding operator is running!!!")
+
+
+# STEP
+sbo_is_running_in_namespace_from_env_step = u'Service Binding Operator is running in [{operator_namespace_env}] namespace'
+
+
+@given(sbo_is_running_in_namespace_from_env_step)
+@when(sbo_is_running_in_namespace_from_env_step)
+def sbo_is_running_in_namespace_from_env(context, operator_namespace_env):
+    env = os.getenv(operator_namespace_env, "")
+    env | should_not.be_none.desc(f"{operator_namespace_env} env variable is set")
+    print(f"{operator_namespace_env} = {env}")
+    sbo_is_running_in_namespace(context, env)
+
+
+# STEP
+sbo_is_running_step = u'Service Binding Operator is running'
+
+
+@given(sbo_is_running_step)
+@when(sbo_is_running_step)
+def sbo_is_running(context):
+    context.namespace | should_not.be_none.desc("Namespace set in context")
+    sbo_is_running_in_namespace(context, context.namespace.name)
+
+
+# STEP
+@given(u'PostgreSQL DB operator is installed')
+def given_db_operator_is_installed(context):
+    db_operator = DbOperator()
+    if not db_operator.is_running():
+        print("DB operator is not installed, installing...")
+        db_operator.install_operator_source() | should.be_truthy.desc("DB operator source installed")
+        db_operator.install_operator_subscription() | should.be_truthy.desc("DB operator subscription installed")
+        db_operator.is_running(wait=True) | should.be_truthy.desc("DB operator installed")
+    print("PostgresSQL DB operator is running!!!")
+
+
+# STEP
+imported_nodejs_app_is_running_step = u'Imported Nodejs application "{application_name}" is running'
+
+
+@given(imported_nodejs_app_is_running_step)
+@when(imported_nodejs_app_is_running_step)
+def imported_nodejs_app_is_running(context, application_name):
+    namespace = context.namespace
+    application = NodeJSApp(application_name, namespace.name)
+    if not application.is_running():
+        print("application is not running, trying to import it")
+        application.install() | should.be_truthy.desc("Application is installed")
+        application.is_running(wait=True) | should.be_truthy.desc("Application is running")
+    print("Nodejs application is running!!!")
+    application.get_db_name_from_api() | should_not.be_none
+    context.nodejs_app_original_generation = application.get_observed_generation()
+    context.nodejs_app_original_pod_name = application.get_running_pod_name()
+    context.nodejs_app = application
+
+
+# STEP
+imported_nodejs_app_is_not_running_step = u'Imported Nodejs application "{application_name}" is not running'
+
+
+@given(imported_nodejs_app_is_not_running_step)
+@when(imported_nodejs_app_is_not_running_step)
+def imported_nodejs_app_is_not_running(context, application_name):
+    namespace = context.namespace
+    application = NodeJSApp(application_name, namespace.name)
+    application.is_running() | should.be_falsy.desc("Aplication not running")
+
+
+# STEP
+db_instance_is_running_step = u'DB "{db_name}" is running'
+
+
+@given(db_instance_is_running_step)
+@when(db_instance_is_running_step)
+def db_instance_is_running(context, db_name):
+    namespace = context.namespace
+
+    db = PostgresDB(db_name, namespace.name)
+    if not db.is_running():
+        db.create() | should.be_truthy.desc("Postgres DB created")
+        db.is_running(wait=True) | should.be_truthy.desc("Postgres DB is running")
+    print(f"DB {db_name} is running!!!")
+
+
+# STEP
+sbr_is_applied_step = u'Service Binding Request is applied to connect the database and the application'
+
+
+@given(sbr_is_applied_step)
+@when(sbr_is_applied_step)
+def sbr_is_applied(context):
+    sbr_yaml = context.text
+    sbr = ServiceBindingRequest()
+    if context.__contains__("nodejs_app"):
+        application = context.nodejs_app
+        context.nodejs_app_original_generation = application.get_observed_generation()
+        context.nodejs_app_original_pod_name = application.get_running_pod_name()
+    sbr.create(sbr_yaml) | should.be_truthy.desc("Service Binding Request Created")
+
+
+# STEP
+@then(u'application should be re-deployed')
+def then_application_redeployed(context):
+    application = context.nodejs_app
+    application.get_redeployed_pod_name(context.nodejs_app_original_pod_name) | should_not.be_none.desc(
+        "There is a running pod of the application different from the original one before redeployment.")
+
+
+# STEP
+@then(u'application should be connected to the DB "{db_name}"')
+def then_app_is_connected_to_db(context, db_name):
+    application = context.nodejs_app
+    app_db_name = application.get_db_name_from_api()
+    app_db_name | should.be_equal_to(db_name)
+
+
+# STEP
+@then(u'jsonpath "{json_path}" of Service Binding Request "{sbr_name}" should be changed to "{json_value_regex}"')
+def then_sbo_jsonpath_is(context, json_path, sbr_name, json_value_regex):
+    openshift = Openshift()
+    openshift.search_resource_in_namespace("servicebindingrequests", sbr_name, context.namespace.name) | should_not.be_none.desc("SBR {sbr_name} exists")
+    result = openshift.get_resource_info_by_jsonpath("sbr", sbr_name, context.namespace.name, json_path, wait=True)
+    result | should_not.be_none.desc("jsonpath result")
+    re.fullmatch(json_value_regex, result) | should_not.be_none.desc("SBO jsonpath result \"{result}\" should match \"{json_value_regex}\"")
+
+
+# STEP
+@then(u'jq "{jq_expression}" of Service Binding Request "{sbr_name}" should be changed to "{json_value_regex}"')
+def then_sbo_jq_is(context, jq_expression, sbr_name, json_value_regex):
+    openshift = Openshift()
+    openshift.search_resource_in_namespace("servicebindingrequests", sbr_name, context.namespace.name) | should_not.be_none.desc("SBR {sbr_name} exists")
+    result = openshift.get_resource_info_by_jq("sbr", sbr_name, context.namespace.name, jq_expression, wait=True)
+    result | should_not.be_none.desc("jq result")
+    re.fullmatch(json_value_regex, result) | should_not.be_none.desc("SBO jq result \"{result}\" should match \"{json_value_regex}\"")
diff --git a/test/e2e/servicebindingrequest_test.go b/test/e2e/servicebindingrequest_test.go
index fb9ff155..d88a6715 100644
--- a/test/e2e/servicebindingrequest_test.go
+++ b/test/e2e/servicebindingrequest_test.go
@@ -24,6 +24,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
 
+	conditionsv1 "github.com/openshift/custom-resource-status/conditions/v1"
 	"github.com/redhat-developer/service-binding-operator/pkg/apis"
 	"github.com/redhat-developer/service-binding-operator/pkg/apis/apps/v1alpha1"
 	"github.com/redhat-developer/service-binding-operator/pkg/controller/servicebindingrequest"
@@ -50,7 +51,7 @@ var (
 	// Intermediate secret should have following data
 	// for postgres operator
 	postgresSecretAssertion = map[string]string{
-		"DATABASE_SECRET_USER":     "user",
+		"DATABASE_SECRET_USERNAME": "user",
 		"DATABASE_SECRET_PASSWORD": "password",
 	}
 
@@ -190,6 +191,7 @@ func assertDeploymentEnvFrom(
 // assertSBRStatus will determine if SBR is on "success" state
 func assertSBRStatus(
 	ctx context.Context,
+	t *testing.T,
 	f *framework.Framework,
 	namespacedName types.NamespacedName,
 ) error {
@@ -198,16 +200,25 @@ func assertSBRStatus(
 		return err
 	}
 
-	for i, condition := range sbr.Status.Conditions {
-		if condition.Type != servicebindingrequest.BindingReady && condition.Status != corev1.ConditionTrue {
-			return fmt.Errorf(
-				"Condition.Type and Condition.Status is '%s' and '%s' instead of '%s' and '%s'",
-				sbr.Status.Conditions[i].Type,
-				sbr.Status.Conditions[i].Status,
-				servicebindingrequest.BindingReady,
-				corev1.ConditionTrue)
-		}
-	}
+	require.True(t,
+		conditionsv1.IsStatusConditionPresentAndEqual(
+			sbr.Status.Conditions,
+			servicebindingrequest.CollectionReady,
+			corev1.ConditionTrue,
+		),
+		"CollectionReady condition should exist and true; existing conditions: %+v",
+		sbr.Status.Conditions,
+	)
+
+	require.True(t,
+		conditionsv1.IsStatusConditionPresentAndEqual(
+			sbr.Status.Conditions,
+			servicebindingrequest.InjectionReady,
+			corev1.ConditionTrue,
+		),
+		"InjectionReady condition should exist and true; existing conditions: %+v",
+		sbr.Status.Conditions,
+	)
 	return nil
 }
 
@@ -292,6 +303,14 @@ func CreateDB(
 	ns := namespacedName.Namespace
 	resourceRef := namespacedName.Name
 
+	// order is important: the operator will follow first the database resource,
+	// then the secret holding the credential; in the case the secret is not
+	// there, default values will be set for each of the contributed
+	// configuration values declared in the Database custom resource definition.
+	t.Log("Creating Database credentials secret mock object...")
+	dbSecret := mocks.SecretMock(ns, secretName, nil)
+	require.NoError(t, f.Client.Create(ctx, dbSecret, cleanupOpts))
+
 	db := mocks.DatabaseCRMock(ns, resourceRef)
 	require.NoError(t, f.Client.Create(ctx, db, cleanupOpts))
 
@@ -309,10 +328,6 @@ func CreateDB(
 		return true
 	}, 10*time.Second, 1*time.Second)
 
-	t.Log("Creating Database credentials secret mock object...")
-	dbSecret := mocks.SecretMock(ns, secretName)
-	require.NoError(t, f.Client.Create(ctx, dbSecret, cleanupOpts))
-
 	return db
 }
 
@@ -497,7 +512,7 @@ func serviceBindingRequestTest(
 	t.Log("Inspecting SBR status...")
 	require.Eventually(t, func() bool {
 		t.Logf("Inspecting SBR: '%s'", sbrNamespacedName)
-		err := assertSBRStatus(todoCtx, f, sbrNamespacedName)
+		err := assertSBRStatus(todoCtx, t, f, sbrNamespacedName)
 		if err != nil {
 			t.Logf("Error on inspecting SBR: '%#v'", err)
 			return false
diff --git a/test/mocks/fake.go b/test/mocks/fake.go
index 85a5de1f..d03fd75a 100644
--- a/test/mocks/fake.go
+++ b/test/mocks/fake.go
@@ -189,8 +189,8 @@ func (f *Fake) AddMockedUnstructuredSecret(name string) *unstructured.Unstructur
 
 // AddNamespacedMockedSecret add mocked object from SecretMock in a namespace
 // which isn't necessarily same as that of the ServiceBindingRequest namespace.
-func (f *Fake) AddNamespacedMockedSecret(name string, namespace string) {
-	f.objs = append(f.objs, SecretMock(namespace, name))
+func (f *Fake) AddNamespacedMockedSecret(name string, namespace string, data map[string][]byte) {
+	f.objs = append(f.objs, SecretMock(namespace, name, data))
 }
 
 // AddMockedUnstructuredConfigMap add mocked object from ConfigMapMock.
diff --git a/test/mocks/mocks.go b/test/mocks/mocks.go
index 210187c3..baf58ffa 100644
--- a/test/mocks/mocks.go
+++ b/test/mocks/mocks.go
@@ -98,7 +98,7 @@ func DatabaseCRDMock(ns string) apiextensionv1beta1.CustomResourceDefinition {
 	FullCRDName := CRDPlural + "." + CRDName
 	annotations := map[string]string{
 		"servicebindingoperator.redhat.io/status.dbCredentials-password": "binding:env:object:secret",
-		"servicebindingoperator.redhat.io/status.dbCredentials-user":     "binding:env:object:secret",
+		"servicebindingoperator.redhat.io/status.dbCredentials-username": "binding:env:object:secret",
 	}
 
 	crd := apiextensionv1beta1.CustomResourceDefinition{
@@ -160,7 +160,7 @@ func PostgresDatabaseCRMock(ns, name string) PostgresDatabase {
 }
 
 func UnstructuredSecretMock(ns, name string) (*unstructured.Unstructured, error) {
-	s := SecretMock(ns, name)
+	s := SecretMock(ns, name, nil)
 	return converter.ToUnstructured(&s)
 }
 
@@ -353,7 +353,14 @@ func UnstructuredDatabaseCRMock(ns, name string) (*unstructured.Unstructured, er
 }
 
 // SecretMock returns a Secret based on PostgreSQL operator usage.
-func SecretMock(ns, name string) *corev1.Secret {
+func SecretMock(ns, name string, data map[string][]byte) *corev1.Secret {
+	if data == nil {
+		data = map[string][]byte{
+			"username": []byte("user"),
+			"password": []byte("password"),
+		}
+	}
+
 	return &corev1.Secret{
 		TypeMeta: metav1.TypeMeta{
 			Kind:       "Secret",
@@ -363,10 +370,7 @@ func SecretMock(ns, name string) *corev1.Secret {
 			Namespace: ns,
 			Name:      name,
 		},
-		Data: map[string][]byte{
-			"user":     []byte("user"),
-			"password": []byte("password"),
-		},
+		Data: data,
 	}
 }
 
@@ -382,7 +386,7 @@ func ConfigMapMock(ns, name string) *corev1.ConfigMap {
 			Name:      name,
 		},
 		Data: map[string]string{
-			"user":     "user",
+			"username": "user",
 			"password": "password",
 		},
 	}
diff --git a/test/third-party-crds/postgresql_v1alpha1_database_crd.yaml b/test/third-party-crds/postgresql_v1alpha1_database_crd.yaml
index a0c4beda..09452ec6 100644
--- a/test/third-party-crds/postgresql_v1alpha1_database_crd.yaml
+++ b/test/third-party-crds/postgresql_v1alpha1_database_crd.yaml
@@ -12,7 +12,7 @@ metadata:
     servicebindingoperator.redhat.io/status.dbConnectionIP: binding:env:attribute
     servicebindingoperator.redhat.io/status.dbConnectionPort: binding:env:attribute
     servicebindingoperator.redhat.io/status.dbCredentials-password: binding:env:object:secret
-    servicebindingoperator.redhat.io/status.dbCredentials-user: binding:env:object:secret
+    servicebindingoperator.redhat.io/status.dbCredentials-username: binding:env:object:secret
     servicebindingoperator.redhat.io/status.dbName: binding:env:attribute
 spec:
   group: postgresql.baiju.dev