Feature 11074: Enhanced OSM declarative modelling for applications. OSM's SDK for intent manipulation

Change-Id: I6d03faa143eafcf30380b3b854c54f177dcf8f25
Signed-off-by: garciadeblas <gerardo.garciadeblas@telefonica.com>
diff --git a/docker/osm-nushell-krm-functions/operations/tests/app.nu b/docker/osm-nushell-krm-functions/operations/tests/app.nu
new file mode 100644
index 0000000..ec5cb18
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/app.nu
@@ -0,0 +1,261 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+# Tests of App instance management
+
+use ../../krm *
+use ../app.nu
+use ../location.nu
+use ../replace.nu
+
+
+# --- all-in-one example (example 1) ---
+
+export def "test app example one" []: [
+    nothing -> nothing
+] {
+    let expected: list<record> = (
+        open artifacts/sw-catalogs/apps/example1/expected_result.yaml
+    )
+
+    let fleet_repos_base: path = (mktemp -t -d)
+
+    let environment: record = {
+        FLEET_REPOS_BASE: $fleet_repos_base
+        CATALOG_REPOS_BASE: ($env.pwd | path join artifacts)
+        APPNAME: myapp01
+        APPNAMESPACE: app-namespace
+        PROFILE_TYPE: apps
+        PROFILE_NAME: mycluster01
+        secret-values-for-postgres-operator-myapp01: {
+            POSTGRES_OPERATOR_HOST: postgres-operator-host
+            POSTGRES_OPERATOR_PORT: 5432
+            POSTGRES_OPERATOR_USER: postgres-operator-user
+            POSTGRES_OPERATOR_PASSWORD: postgres-operator-password
+        }
+        secret-values-for-postgres-operator-ui-myapp01: {
+            POSTGRES_OPERATOR_UI_HOST: postgres-operator-ui-host
+            POSTGRES_OPERATOR_UI_PORT: 8080
+            POSTGRES_OPERATOR_UI_USER: postgres-operator-ui-user
+            POSTGRES_OPERATOR_UI_PASSWORD: postgres-operator-ui-password
+        }
+    }
+
+    let actual: list<record> = (
+        open artifacts/sw-catalogs/apps/example1/app-instance-from-model.yaml
+        | app create --dry-run $environment
+    )
+
+    # Overwrites the encrypted part of the secrets in both
+    let actual_trimmed: list<record> = (
+        $actual
+        # For each KSU's ResourceList
+        | each {|k|
+            $k
+            # Delete sops key from all secrets
+            | ( patch resource reject key $.sops '' Secret )
+            # Replace encrypted value by empty string
+            | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+        }
+    )
+    let expected_trimmed: list<record> = (
+        $expected
+        # For each KSU's ResourceList
+        | each {|k|
+            $k
+            # Delete sops key from all secrets
+            | ( patch resource reject key $.sops '' Secret )
+            # Replace encrypted value by empty string
+            | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+        }
+    )
+
+    # Checks
+    assert equal $actual_trimmed $expected_trimmed
+
+    # Cleanup
+    rm -rf $fleet_repos_base
+}
+
+
+# --- all-in-one example (example 2) ---
+
+export def "test app example two" []: [
+    nothing -> nothing
+] {
+    let expected: list<record> = (
+        open artifacts/sw-catalogs/apps/example2/expected_result.yaml
+    )
+    let fleet_repos_base: path = (mktemp -t -d)
+
+    let environment: record = {
+        FLEET_REPOS_BASE: $fleet_repos_base
+        CATALOG_REPOS_BASE: ($env.pwd | path join artifacts)
+        APPNAME: myapp02
+        APPNAMESPACE: app-namespace
+        PROFILE_TYPE: apps
+        PROFILE_NAME: mycluster02
+        secret-values-for-postgres-operator-myapp02: {
+            POSTGRES_OPERATOR_HOST: postgres-operator-host
+            POSTGRES_OPERATOR_PORT: 5432
+            POSTGRES_OPERATOR_USER: postgres-operator-user
+            POSTGRES_OPERATOR_PASSWORD: postgres-operator-password
+        }
+        secret-values-for-postgres-operator-ui-myapp01: {
+            POSTGRES_OPERATOR_UI_HOST: postgres-operator-ui-host
+            POSTGRES_OPERATOR_UI_PORT: 8080
+            POSTGRES_OPERATOR_UI_USER: postgres-operator-ui-user
+            POSTGRES_OPERATOR_UI_PASSWORD: postgres-operator-ui-password
+        }
+    }
+
+    let actual: list<record> = (
+        open artifacts/sw-catalogs/apps/example2/app-instance-from-model.yaml
+        | app create --dry-run $environment
+    )
+
+    # Overwrites the encrypted part of the secrets in both
+    let actual_trimmed: list<record> = (
+        $actual
+        # For each KSU's ResourceList
+        | each {|k|
+            $k
+            # Delete sops key from all secrets
+            | ( patch resource reject key $.sops '' Secret )
+            # Replace encrypted value by empty string
+            | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+        }
+    )
+    let expected_trimmed: list<record> = (
+        $expected
+        # For each KSU's ResourceList
+        | each {|k|
+            $k
+            # Delete sops key from all secrets
+            # | ( patch resource reject key $.sops '' Secret )
+            # Replace encrypted value by empty string
+            # | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+        }
+    )
+
+    # Checks
+    assert equal $actual_trimmed $expected_trimmed
+
+    # Cleanup
+    rm -rf $fleet_repos_base
+}
+
+
+export def "test app example two written to folder" []: [
+    nothing -> nothing
+] {
+    let expected: list<record> = (
+        open artifacts/sw-catalogs/apps/example2/expected_result.yaml
+    )
+    let fleet_repos_base: path = (mktemp -t -d)
+
+    let environment: record = {
+        FLEET_REPOS_BASE: $fleet_repos_base
+        CATALOG_REPOS_BASE: ($env.pwd | path join artifacts)
+        APPNAME: myapp02
+        APPNAMESPACE: app-namespace
+        PROFILE_TYPE: apps
+        PROFILE_NAME: mycluster02
+        secret-values-for-postgres-operator-myapp02: {
+            POSTGRES_OPERATOR_HOST: postgres-operator-host
+            POSTGRES_OPERATOR_PORT: 5432
+            POSTGRES_OPERATOR_USER: postgres-operator-user
+            POSTGRES_OPERATOR_PASSWORD: postgres-operator-password
+        }
+        secret-values-for-postgres-operator-ui-myapp01: {
+            POSTGRES_OPERATOR_UI_HOST: postgres-operator-ui-host
+            POSTGRES_OPERATOR_UI_PORT: 8080
+            POSTGRES_OPERATOR_UI_USER: postgres-operator-ui-user
+            POSTGRES_OPERATOR_UI_PASSWORD: postgres-operator-ui-password
+        }
+    }
+
+    # Retrieve instance model
+    let instance_model: record = (open artifacts/sw-catalogs/apps/example2/app-instance-from-model.yaml)
+
+    # Write to folder
+    $instance_model | app create $environment
+
+    # Calculate the actual ResourceLists from the target folders
+    let targets: list<string> = (
+        $instance_model
+        | replace vars $environment
+        | get $.spec.ksus
+        | each {|k|
+            $k
+            | get "target"
+            | location to absolute path
+        }
+    )
+    let actual: list<record> = (
+        $targets
+        | each {|t|
+            {} | convert folder to resourcelist $t
+        }
+    )
+
+    # Overwrites the encrypted part of the secrets in both
+    let actual_trimmed: list<record> = (
+        $actual
+        # For each KSU's ResourceList
+        | each {|k|
+            $k
+            # Delete sops key from all secrets
+            | ( patch resource reject key $.sops '' Secret )
+            # Replace encrypted value by empty string
+            | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+        }
+    )
+    let expected_trimmed: list<record> = (
+        $expected
+        # For each KSU's ResourceList
+        | each {|k|
+            $k
+            # Delete sops key from all secrets
+            # | ( patch resource reject key $.sops '' Secret )
+            # Replace encrypted value by empty string
+            # | ( patch resource update key $.data."values.yaml" 'ENCRYPTED' '' Secret )
+        }
+    )
+
+    # Ensures that the items from both ResourceLists are sorted in the same order and removes irrelevant indexes and keys from `kpt`
+    let actual_fixed: list<record> = ($actual_trimmed | get $.items.0 | sort-by $.kind | sort-by $.metadata.name
+    | each {|k|
+        $k
+        | reject -i $.metadata.annotations."config.kubernetes.io/index"
+        | reject -i $.metadata.annotations."internal.config.kubernetes.io/index"
+        | reject -i $.metadata.annotations."internal.config.kubernetes.io/seqindent"
+    })
+    let expected_fixed = ($expected_trimmed | get $.items.0 | sort-by $.kind | sort-by $.metadata.name
+    | each {|k|
+        $k
+        | reject -i $.metadata.annotations."config.kubernetes.io/index"
+        | reject -i $.metadata.annotations."internal.config.kubernetes.io/index"
+        | reject -i $.metadata.annotations."internal.config.kubernetes.io/seqindent"
+    })
+
+    # Checks
+    assert equal $actual_fixed $expected_fixed
+
+    # Cleanup
+    rm -rf $fleet_repos_base
+}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/app-instance-from-model.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/app-instance-from-model.yaml
new file mode 100644
index 0000000..dcd06ac
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/app-instance-from-model.yaml
@@ -0,0 +1,169 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+apiVersion: osm.softwaredefinition.io/v1alpha1
+# kind: AppBlueprint
+kind: AppInstantiation
+metadata:
+  name: example1-app
+spec:
+  # description: "Example App Blueprint"
+  description: "Example App Instantiation"
+  version: "1.0.0"
+  ksus:
+  - name: main
+    target:
+      # Absolute path to KSU folder: repo-path + base-path + relative-path
+      repo-path:
+      - ${FLEET_REPOS_BASE}     # Default: `/repos`
+      - fleet             # Repo name.
+      base-path:
+      - ${PROJECT_NAME}   # Project name.
+      - ${PROFILE_TYPE}
+      - ${PROFILE_NAME}
+      relative-path:      # App folder + ksu folder (in case of multiple KSUs)
+      - ${APPNAME}
+      - main
+    patterns:
+    - name: main-pattern
+      source:
+        # Absolute path to OKA folder: repo-path + base-path + relative-path
+        repo-path:
+        - ${CATALOG_REPOS_BASE}     # Default: `/repos`
+        - sw-catalogs       # Repo name
+        base-path:          # Absolute path to OKA folder
+        - apps              # OKA type
+        - example1          # OKA folder
+        relative-path:      # Pattern template folder (default: `templates/`)
+        - templates
+        - main-pattern
+      bricks:
+      - name: main-brick
+        type: basic
+        # type: HelmReleaseSet
+        kustomization:
+          name: main-kustomization-${APPNAME}
+          # OPTIONAL:
+          # namespace: flux-system
+        source:             # Absolute path to manifests referenced by the Kustomization (brick folder): base-path + relative-path
+          repo-path:
+          - ${CATALOG_REPOS_BASE}     # Default: `/repos`
+          - sw-catalogs       # Repo name
+          base-path:          # Absolute path to OKA folder
+          - apps              # OKA type
+          - example1          # OKA folder
+          relative-path:
+          - manifests
+          - main-pattern
+          - main-brick-manifests
+      - name: database-brick
+        type: HelmReleaseSet
+        kustomization:
+          name: database-kustomization-${APPNAME}
+          # OPTIONAL:
+          # namespace: flux-system
+        source:
+          repo-path:
+          - ${CATALOG_REPOS_BASE}     # Default: `/repos`
+          - sw-catalogs       # Repo name
+          base-path:          # Absolute path to OKA folder
+          - apps              # OKA type
+          - example1          # OKA folder
+          relative-path:
+          - manifests
+          - main-pattern
+          - database-manifests
+        public-age-key: age18juyaw9kvzgkpqx8kun2n7qtvqva6ajj7ulse435thkgnlfjhf2qj76h64
+        hrset-values:
+          - name: db-operator
+            HelmRelease:
+              name: postgres-operator-${APPNAME}
+              namespace: ${APPNAMESPACE}
+            inline-values:
+              key1: value1
+              key2: value2
+            valuesFrom:
+              configMapKeyRef:
+                name: postgres-operator-cm-${APPNAME}
+                # OPTIONAL:
+                # key: values.yaml
+              secretKeyRef:
+                name: postgres-operator-secret-${APPNAME}
+                # OPTIONAL:
+                # key: values.yaml
+            create-cm:      # If the map is empty, the ConfigMap will not be created
+              cm-key1: cm-value1
+              cm-key2: cm-value2
+            create-secret:  # If the map is empty, the Secret will not be created
+              env-values-reference: secret-values-for-postgres-operator-${APPNAME}
+          - name: db-op-user-interface
+            HelmRelease:
+              name: postgres-operator-ui-${APPNAME}
+              namespace: ${APPNAMESPACE}
+            inline-values:
+              key1: value1
+              key2: value2
+            valuesFrom:
+              configMapKeyRef:
+                name: postgres-operator-ui-cm-${APPNAME}
+                # OPTIONAL:
+                # key: values.yaml
+              secretKeyRef:
+                name: postgres-operator-ui-secret-${APPNAME}
+                # OPTIONAL:
+                # key: values.yaml
+            create-cm:      # If the map is empty, the ConfigMap will not be created
+              cm-key1: cm-value1
+              cm-key2: cm-value2
+            create-secret:  # If the map is empty, the Secret will not be created
+              env-values-reference: secret-values-for-postgres-operator-ui-${APPNAME}
+  # parameters:
+  #   - name: replicaCount
+  #     description: "Number of replicas"
+  #     # default: 1
+  #     value: 1
+  #   - name: ingressHost
+  #     description: "Ingress hostname"
+  #     value: "ingress for-${APPNAME}"
+  # readinessChecks:
+  #   - resource:
+  #       kind: Deployment
+  #       name: main-deployment-${APPNAME}
+  #     condition:
+  #       type: Available
+  #       status: "True"
+  #   - resource:
+  #       kind: Service
+  #       name: main-service
+  #     condition:
+  #       type: Ready
+  # TODO:
+  # outputValues:
+  #   - name: apiEndpoint
+  #     valueFrom:
+  #       serviceIP:
+  #         name: main-service
+  #   - name: adminPassword
+  #     valueFrom:
+  #       secretKeyRef:
+  #         name: app-secrets
+  #         key: admin-password
+  # TODO:
+  # dependencies:
+  #   - name: nginx-ingress
+  #     type: InfraController
+  #     version: ">=1.0.0"
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/expected_result.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/expected_result.yaml
new file mode 100644
index 0000000..ae6fa90
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/expected_result.yaml
@@ -0,0 +1,218 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+- apiVersion: config.kubernetes.io/v1
+  kind: ResourceList
+  items:
+  - apiVersion: kustomize.toolkit.fluxcd.io/v1
+    kind: Kustomization
+    metadata:
+      name: database-kustomization-myapp01
+      namespace: flux-system
+      annotations:
+        config.kubernetes.io/index: '0'
+        config.kubernetes.io/path: ks-database.yaml
+        internal.config.kubernetes.io/index: '0'
+        internal.config.kubernetes.io/path: ks-database.yaml
+        internal.config.kubernetes.io/seqindent: compact
+    spec:
+      interval: 1h0m0s
+      path: apps/example1/manifests/main-pattern/database-manifests
+      prune: true
+      sourceRef:
+        kind: GitRepository
+        name: sw-catalogs
+        namespace: flux-system
+      targetNamespace: app-namespace
+      postBuild:
+        substitute:
+          appname: myapp01
+          appnamespace: app-namespace
+      wait: true
+      patches:
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-myapp01
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/values
+            value:
+              key1: value1
+              key2: value2
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-myapp01
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: ConfigMap
+              name: postgres-operator-cm-myapp01
+              key: values.yaml
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-myapp01
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: Secret
+              name: postgres-operator-secret-myapp01
+              key: values.yaml
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-ui-myapp01
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/values
+            value:
+              key1: value1
+              key2: value2
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-ui-myapp01
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: ConfigMap
+              name: postgres-operator-ui-cm-myapp01
+              key: values.yaml
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-ui-myapp01
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: Secret
+              name: postgres-operator-ui-secret-myapp01
+              key: values.yaml
+  - apiVersion: kustomize.toolkit.fluxcd.io/v1
+    kind: Kustomization
+    metadata:
+      name: main-kustomization-myapp01
+      namespace: flux-system
+      annotations:
+        config.kubernetes.io/index: '0'
+        config.kubernetes.io/path: ks-main.yaml
+        internal.config.kubernetes.io/index: '0'
+        internal.config.kubernetes.io/path: ks-main.yaml
+        internal.config.kubernetes.io/seqindent: compact
+    spec:
+      interval: 1h0m0s
+      path: apps/example1/manifests/main-pattern/main-brick-manifests
+      prune: true
+      sourceRef:
+        kind: GitRepository
+        name: sw-catalogs
+        namespace: flux-system
+      targetNamespace: app-namespace
+      postBuild:
+        substitute:
+          app_name: myapp01
+      wait: true
+  - apiVersion: v1
+    kind: ConfigMap
+    metadata:
+      name: postgres-operator-cm-myapp01
+      namespace: app-namespace
+      annotations:
+        config.kubernetes.io/path: postgres-operator-cm-myapp01.yaml
+        internal.config.kubernetes.io/path: postgres-operator-cm-myapp01.yaml
+    data:
+      values.yaml: |-
+        cm-key1: cm-value1
+        cm-key2: cm-value2
+  - apiVersion: v1
+    kind: Secret
+    metadata:
+      name: postgres-operator-secret-myapp01
+      namespace: app-namespace
+      annotations:
+        config.kubernetes.io/path: postgres-operator-secret-myapp01.yaml
+        internal.config.kubernetes.io/path: postgres-operator-secret-myapp01.yaml
+    data:
+      values.yaml: ENC[AES256_GCM,data:wzm5y3kpbk+ZVM2TsNd6D4rmEZ7FU5jxM3dZiGILjZ7Hy00oS0ua9nwc0WU+IwSz0S4m7RiETYRnWITELpq0xoogCXRVIz7SkOivWE6/lv+H0SgQOqZ0iDHZgE/kly8S61kh1QGAeCCUZcpUyXwjgZ1S/iwZqAlVl2cizkGvYMMdgJAGBqTrXakM6LH10J5JBZX/05Cy9Y1rSzGDl6XsJwpnj0znyYIAGsq97nsjctzVGUX0TTIvE03de+T5ZB/nxOZrpcsUSbrbHBj4J5/uijOzV4NCjZ6OV7PWl64fKlpVcLt8z5geO9RMVBT/WlhV,iv:bGxvvh4umOmM7iRHiu0i6lWjWeD1yOkhPGSiMGDgkq0=,tag:v0Og8/hjsEuPLB1wmXSWSw==,type:str]
+    sops:
+      kms: []
+      gcp_kms: []
+      azure_kv: []
+      hc_vault: []
+      age:
+      - recipient: age18juyaw9kvzgkpqx8kun2n7qtvqva6ajj7ulse435thkgnlfjhf2qj76h64
+        enc: |
+          -----BEGIN AGE ENCRYPTED FILE-----
+          YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHV3d4R2JzS0VmazdCUXBR
+          citpNE9uVmdGbm8vNWdCK3dQR2NObFRaQlVFCjJJZGU5b25pRC8zQ0NOT1J1eEcr
+          bzBzdHE5ZlpBelJaSnpLVGlhcmp3QUkKLS0tIC9Fam5DcTZHQ0pzK3o5N0o2MVJr
+          c3krQTlvU2FXckxxTUkraDBRMWJwd3cK0sd3dx5Arzh4XxJVUFOi1cPnF9UIsv18
+          VXKeyoIOK8pvPS5c4UvCTfBFdYs69Sg6+6FfbCoFJepaT+VfaM1XPA==
+          -----END AGE ENCRYPTED FILE-----
+      lastmodified: 2025-04-29T18:26:22Z
+      mac: ENC[AES256_GCM,data:io3iCpdT61n7ECEkyM2KhBgp9oZ49I2YBjuUA8HQSLyDDzFrNNeZh0bVOWI3HT89egkZDt8nmj8+A4Bx9sC666iBcqcFx+VuH//Db7SZXYJXFm1qNP+lt+3ic4fesJgYKG5ld+7FtB1ApKccmE7Ri0yrWGYsrMJh2aRK6T3VyzQ=,iv:9lvXUEPwIZxphwEc91QCPPSxkVNIiUAZWQWby502FJA=,tag:0IxYm+VLLKOoZ80FQlLspQ==,type:str]
+      pgp: []
+      encrypted_regex: ^(data|stringData)$
+      version: 3.8.1
+  - apiVersion: v1
+    kind: ConfigMap
+    metadata:
+      name: postgres-operator-ui-cm-myapp01
+      namespace: app-namespace
+      annotations:
+        config.kubernetes.io/path: postgres-operator-ui-cm-myapp01.yaml
+        internal.config.kubernetes.io/path: postgres-operator-ui-cm-myapp01.yaml
+    data:
+      values.yaml: |-
+        cm-key1: cm-value1
+        cm-key2: cm-value2
+  - apiVersion: v1
+    kind: Secret
+    metadata:
+      name: postgres-operator-ui-secret-myapp01
+      namespace: app-namespace
+      annotations:
+        config.kubernetes.io/path: postgres-operator-ui-secret-myapp01.yaml
+        internal.config.kubernetes.io/path: postgres-operator-ui-secret-myapp01.yaml
+    data:
+      values.yaml: ENC[AES256_GCM,data:37zRz8FafCMe/cfZGI9Ojx+DP1Y09C5C4P6E4wkPZJfqb8brliWAzQ14cOHZgeOQBugcuguMLTacGz+QCDvU9u5ZFs9ouXY3pWbs9wa4Ywoaum8T2gK1edzEoQ9U0QG5wqndwti4p1wo8Im6+A/VOpFXcEGXdzSLfxcryTGA5EXBtwG9/SvhTvx6CCZGvWcNtjqjJjwNOOtFyJJFBuc7CrWFgYliSwwZ2q4INJhDTrrQ81zKDzdkW/ac+uapey/XQ3lYK56n7K+Z+hxoBJLYRfsRdtBCXxXpThfYXPu608WDHUcedjitATsk7NwA/F+mqmm1T5FO7ePspSaTZgvhBMi+OP1HyIgKbmiEhg==,iv:dVYZTjkNYAgNpFUvFTIiyEFM51tv45G1yp1NPG/xJKQ=,tag:xcrQZq1swfcjfPjcNgN0JQ==,type:str]
+    sops:
+      kms: []
+      gcp_kms: []
+      azure_kv: []
+      hc_vault: []
+      age:
+      - recipient: age18juyaw9kvzgkpqx8kun2n7qtvqva6ajj7ulse435thkgnlfjhf2qj76h64
+        enc: |
+          -----BEGIN AGE ENCRYPTED FILE-----
+          YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqUS9vaVRVbk9DTkpreVNU
+          Y0t6TW9sbk9Ua1hWSUdVa0hrOWVVSkZIbkVnCnpVcUpxdkQweWw1MjhBVldkaXVX
+          OTRlYW9jQ3ZxNnhiMGExanJYNU9oREkKLS0tIGl3U201ZmNPM2xaY0FyOWZsZGNs
+          aFBQTU1BK3BTSWZ6djFMRDFkbjkrYnMKX/Uo7ePikgbnOfewdHSmCXE4aMfZU0IQ
+          jr9ZlHVGPvaS6yy7fezVZOJI++nwF7fyXZfJW9mm0rSRBy5vnKQYNg==
+          -----END AGE ENCRYPTED FILE-----
+      lastmodified: 2025-04-29T18:26:22Z
+      mac: ENC[AES256_GCM,data:h0ZEFSy0jijGOmjKIFWi5GOfGYgSCWg2BNgMFXT2AwwR2j/xcJWBwFvKPzy7X028Onz0aNmnPa7hf4eI8HS2pp4FEeUbTp8Z+X8it01XDbhmXqnykeHRU40CaKtqaNBbT2uKa/jpT5KgZl+2mnLUL9VU1vlu7BFIQrYPPtOTxgI=,iv:cBSpvEZHFGEAVqWyXUF9OtmuwN4DM3nS3ZgJepUknZ0=,tag:W2utUcIePQpqHkZYV4FiFA==,type:str]
+      pgp: []
+      encrypted_regex: ^(data|stringData)$
+      version: 3.8.1
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/hrset-database.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/hrset-database.yaml
new file mode 100644
index 0000000..8623232
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/hrset-database.yaml
@@ -0,0 +1,58 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+---
+apiVersion: helm.toolkit.fluxcd.io/v2beta1
+kind: HelmRelease
+metadata:
+  name: postgres-operator-${appname}
+  namespace: ${appnamespace}
+spec:
+  chart:
+    spec:
+      chart: postgres-operator
+      reconcileStrategy: ChartVersion
+      sourceRef:
+        kind: HelmRepository
+        name: postgres-operator-charts-${appname}
+        namespace: ${appnamespace}
+  interval: 3m0s
+  targetNamespace: ${appnamespace}
+  values: {}
+
+---
+
+apiVersion: helm.toolkit.fluxcd.io/v2beta1
+kind: HelmRelease
+metadata:
+  name: postgres-operator-ui-${appname}
+  namespace: ${appnamespace}
+spec:
+  dependsOn:
+  - name: postgres-operator-${appname}
+    namespace: ${appnamespace}
+  chart:
+    spec:
+      chart: postgres-operator-ui
+      reconcileStrategy: ChartVersion
+      sourceRef:
+        kind: HelmRepository
+        name: postgres-operator-charts-${appname}
+        namespace: ${appnamespace}
+  interval: 3m0s
+  targetNamespace: ${appnamespace}
+  values: {}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml
new file mode 100644
index 0000000..eaf2dda
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml
@@ -0,0 +1,29 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+---
+apiVersion: source.toolkit.fluxcd.io/v1beta2
+kind: HelmRepository
+metadata:
+  name: postgres-operator-charts-${appname}
+  namespace: ${appnamespace}
+spec:
+  interval: 10m0s
+  # type: oci
+  # url: oci://registry-1.docker.io/bitnamicharts
+  type: default
+  url: https://opensource.zalando.com/postgres-operator/charts/postgres-operator
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/main-brick-manifests/configmap.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/main-brick-manifests/configmap.yaml
new file mode 100644
index 0000000..5bd5c50
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/manifests/main-pattern/main-brick-manifests/configmap.yaml
@@ -0,0 +1,23 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: my-cm-${appname}
+data:
+  my-key: my-value
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-database.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-database.yaml
new file mode 100644
index 0000000..af2831c
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-database.yaml
@@ -0,0 +1,36 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+  name: database-kustomization-${APPNAME}
+  namespace: flux-system
+spec:
+  interval: 1h0m0s
+  path: ./apps/example1/manifests/main-pattern/database-manifests
+  prune: true
+  sourceRef:
+    kind: GitRepository
+    name: sw-catalogs
+    namespace: flux-system
+  targetNamespace: ${APPNAMESPACE}
+  postBuild:
+    substitute:
+      appname: ${APPNAME}
+      appnamespace: ${APPNAMESPACE}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-main.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-main.yaml
new file mode 100644
index 0000000..d80fe76
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example1/templates/main-pattern/ks-main.yaml
@@ -0,0 +1,35 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+  name: main-kustomization-${APPNAME}
+  namespace: flux-system
+spec:
+  interval: 1h0m0s
+  path: ./apps/example1/manifests/main-pattern/main-brick-manifests
+  prune: true
+  sourceRef:
+    kind: GitRepository
+    name: sw-catalogs
+    namespace: flux-system
+  targetNamespace: ${APPNAMESPACE}
+  postBuild:
+    substitute:
+      app_name: ${APPNAME}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/app-instance-from-model.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/app-instance-from-model.yaml
new file mode 100644
index 0000000..fe248e4
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/app-instance-from-model.yaml
@@ -0,0 +1,270 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+apiVersion: osm.softwaredefinition.io/v1alpha1
+# kind: AppBlueprint
+kind: AppInstantiation
+metadata:
+  name: example2-app
+spec:
+  # description: "Example App Blueprint"
+  description: "Example App Instantiation"
+  version: "1.0.0"
+  ksus:
+  - name: main
+    target:
+      # Absolute path to KSU folder: repo-path + base-path + relative-path
+      repo-path:
+      - ${FLEET_REPOS_BASE}     # Default: `/repos`
+      - fleet             # Repo name.
+      base-path:
+      - ${PROJECT_NAME}   # Project name.
+      - ${PROFILE_TYPE}
+      - ${PROFILE_NAME}
+      relative-path:      # App folder + ksu folder (in case of multiple KSUs)
+      - ${APPNAME}
+      - main
+    patterns:
+    - name: active-pattern
+      source:
+        # Absolute path to OKA folder: repo-path + base-path + relative-path
+        repo-path:
+        - ${CATALOG_REPOS_BASE}     # Default: `/repos`
+        - sw-catalogs       # Repo name
+        base-path:          # Absolute path to OKA folder
+        - apps              # OKA type
+        - example2          # OKA folder
+        relative-path:      # Pattern template folder (default: `templates/`)
+        - templates
+        - main-pattern
+      # Pattern-specific parameters, to be added as needed (optional)
+      parameters:
+        EXAMPLE_PARAMETER1: active-example-value1
+        EXAMPLE_PARAMETER2: active-example-value2
+      bricks:
+      - name: main-brick
+        type: basic
+        # type: HelmReleaseSet
+        kustomization:
+          name: ks-${APPNAME}-${PATTERN_NAME}
+          # OPTIONAL:
+          # namespace: flux-system
+        source:             # Absolute path to manifests referenced by the Kustomization (brick folder): base-path + relative-path
+          repo-path:
+          - ${CATALOG_REPOS_BASE}     # Default: `/repos`
+          - sw-catalogs       # Repo name
+          base-path:          # Absolute path to OKA folder
+          - apps              # OKA type
+          - example2          # OKA folder
+          relative-path:
+          - manifests
+          - main-pattern
+          - main-brick-manifests
+      - name: database-brick
+        type: HelmReleaseSet
+        kustomization:
+          name: database-kustomization-${APPNAME}-${PATTERN_NAME}
+          # OPTIONAL:
+          # namespace: flux-system
+        source:
+          repo-path:
+          - ${CATALOG_REPOS_BASE}     # Default: `/repos`
+          - sw-catalogs       # Repo name
+          base-path:          # Absolute path to OKA folder
+          - apps              # OKA type
+          - example2          # OKA folder
+          relative-path:
+          - manifests
+          - main-pattern
+          - database-manifests
+        # To be inserted
+        public-age-key: age18juyaw9kvzgkpqx8kun2n7qtvqva6ajj7ulse435thkgnlfjhf2qj76h64
+        hrset-values:
+          - name: db-operator
+            HelmRelease:
+              name: postgres-operator-${APPNAME}-${PATTERN_NAME}
+              namespace: ${APPNAMESPACE}
+            inline-values:
+              key1: value1
+              key2: value2
+            valuesFrom:
+              configMapKeyRef:
+                name: postgres-operator-cm-${APPNAME}-${PATTERN_NAME}
+                # OPTIONAL:
+                # key: values.yaml
+              secretKeyRef:
+                name: postgres-operator-secret-${APPNAME}-${PATTERN_NAME}
+                # OPTIONAL:
+                # key: values.yaml
+            create-cm:      # If the map is empty, the ConfigMap will not be created
+              cm-key1: cm-value1
+              cm-key2: cm-value2
+            create-secret:  # If the map is empty, the Secret will not be created
+              env-values-reference: secret-values-for-postgres-operator-${APPNAME}
+          - name: db-op-user-interface
+            HelmRelease:
+              name: postgres-operator-ui-${APPNAME}-${PATTERN_NAME}
+              namespace: ${APPNAMESPACE}
+            inline-values:
+              key1: value1
+              key2: value2
+            valuesFrom:
+              configMapKeyRef:
+                name: postgres-operator-ui-cm-${APPNAME}-${PATTERN_NAME}
+                # OPTIONAL:
+                # key: values.yaml
+              secretKeyRef:
+                name: postgres-operator-ui-secret-${APPNAME}-${PATTERN_NAME}
+                # OPTIONAL:
+                # key: values.yaml
+            create-cm:      # If the map is empty, the ConfigMap will not be created
+              cm-key1: cm-value1
+              cm-key2: cm-value2
+            create-secret:  # If the map is empty, the Secret will not be created
+              env-values-reference: secret-values-for-postgres-operator-ui-${APPNAME}
+    - name: standby-pattern
+      source:
+        # Absolute path to OKA folder: repo-path + base-path + relative-path
+        repo-path:
+        - ${CATALOG_REPOS_BASE}     # Default: `/repos`
+        - sw-catalogs       # Repo name
+        base-path:          # Absolute path to OKA folder
+        - apps              # OKA type
+        - example2          # OKA folder
+        relative-path:      # Pattern template folder (default: `templates/`)
+        - templates
+        - main-pattern
+      # Pattern-specific parameters, to be added as needed (optional)
+      parameters:
+        EXAMPLE_PARAMETER1: standby-example-value1
+        EXAMPLE_PARAMETER2: standby-example-value2
+      bricks:
+      - name: main-brick
+        type: basic
+        # type: HelmReleaseSet
+        kustomization:
+          name: ks-${APPNAME}-${PATTERN_NAME}
+          # OPTIONAL:
+          # namespace: flux-system
+        source:             # Absolute path to manifests referenced by the Kustomization (brick folder): base-path + relative-path
+          repo-path:
+          - ${CATALOG_REPOS_BASE}     # Default: `/repos`
+          - sw-catalogs       # Repo name
+          base-path:          # Absolute path to OKA folder
+          - apps              # OKA type
+          - example2          # OKA folder
+          relative-path:
+          - manifests
+          - main-pattern
+          - main-brick-manifests
+      - name: database-brick
+        type: HelmReleaseSet
+        kustomization:
+          name: database-kustomization-${APPNAME}-${PATTERN_NAME}
+          # OPTIONAL:
+          # namespace: flux-system
+        source:
+          repo-path:
+          - ${CATALOG_REPOS_BASE}     # Default: `/repos`
+          - sw-catalogs       # Repo name
+          base-path:          # Absolute path to OKA folder
+          - apps              # OKA type
+          - example2          # OKA folder
+          relative-path:
+          - manifests
+          - main-pattern
+          - database-manifests
+        public-age-key: age18juyaw9kvzgkpqx8kun2n7qtvqva6ajj7ulse435thkgnlfjhf2qj76h64
+        hrset-values:
+          - name: db-operator
+            HelmRelease:
+              name: postgres-operator-${APPNAME}-${PATTERN_NAME}
+              namespace: ${APPNAMESPACE}
+            inline-values:
+              key1: value1
+              key2: value2
+            valuesFrom:
+              configMapKeyRef:
+                name: postgres-operator-cm-${APPNAME}-${PATTERN_NAME}
+                # OPTIONAL:
+                # key: values.yaml
+              secretKeyRef:
+                name: postgres-operator-secret-${APPNAME}-${PATTERN_NAME}
+                # OPTIONAL:
+                # key: values.yaml
+            create-cm:      # If the map is empty, the ConfigMap will not be created
+              cm-key1: cm-value1
+              cm-key2: cm-value2
+            create-secret:  # If the map is empty, the Secret will not be created
+              env-values-reference: secret-values-for-postgres-operator-${APPNAME}
+          - name: db-op-user-interface
+            HelmRelease:
+              name: postgres-operator-ui-${APPNAME}-${PATTERN_NAME}
+              namespace: ${APPNAMESPACE}
+            inline-values:
+              key1: value1
+              key2: value2
+            valuesFrom:
+              configMapKeyRef:
+                name: postgres-operator-ui-cm-${APPNAME}-${PATTERN_NAME}
+                # OPTIONAL:
+                # key: values.yaml
+              secretKeyRef:
+                name: postgres-operator-ui-secret-${APPNAME}-${PATTERN_NAME}
+                # OPTIONAL:
+                # key: values.yaml
+            create-cm:      # If the map is empty, the ConfigMap will not be created
+              cm-key1: cm-value1
+              cm-key2: cm-value2
+            create-secret:  # If the map is empty, the Secret will not be created
+              env-values-reference: secret-values-for-postgres-operator-ui-${APPNAME}
+  # parameters:
+  #   - name: replicaCount
+  #     description: "Number of replicas"
+  #     # default: 1
+  #     value: 1
+  #   - name: ingressHost
+  #     description: "Ingress hostname"
+  #     value: "ingress for-${APPNAME}"
+  # readinessChecks:
+  #   - resource:
+  #       kind: Deployment
+  #       name: main-deployment-${APPNAME}
+  #     condition:
+  #       type: Available
+  #       status: "True"
+  #   - resource:
+  #       kind: Service
+  #       name: main-service
+  #     condition:
+  #       type: Ready
+  # TODO:
+  # outputValues:
+  #   - name: apiEndpoint
+  #     valueFrom:
+  #       serviceIP:
+  #         name: main-service
+  #   - name: adminPassword
+  #     valueFrom:
+  #       secretKeyRef:
+  #         name: app-secrets
+  #         key: admin-password
+  # TODO:
+  # dependencies:
+  #   - name: nginx-ingress
+  #     type: InfraController
+  #     version: ">=1.0.0"
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/expected_result.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/expected_result.yaml
new file mode 100644
index 0000000..464b258
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/expected_result.yaml
@@ -0,0 +1,320 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+- apiVersion: config.kubernetes.io/v1
+  kind: ResourceList
+  items:
+  - apiVersion: kustomize.toolkit.fluxcd.io/v1
+    kind: Kustomization
+    metadata:
+      name: database-kustomization-myapp02-active-pattern
+      namespace: flux-system
+      annotations:
+        config.kubernetes.io/index: '0'
+        config.kubernetes.io/path: ks-database.yaml
+        internal.config.kubernetes.io/index: '0'
+        internal.config.kubernetes.io/path: ks-database.yaml
+        internal.config.kubernetes.io/seqindent: compact
+    spec:
+      interval: 1h0m0s
+      path: apps/example2/manifests/main-pattern/database-manifests
+      prune: true
+      sourceRef:
+        kind: GitRepository
+        name: sw-catalogs
+        namespace: flux-system
+      targetNamespace: app-namespace
+      postBuild:
+        substitute:
+          appname: myapp02
+          appnamespace: app-namespace
+      wait: true
+      patches:
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-myapp02-active-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/values
+            value:
+              key1: value1
+              key2: value2
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-myapp02-active-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: ConfigMap
+              name: postgres-operator-cm-myapp02-active-pattern
+              key: values.yaml
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-myapp02-active-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: Secret
+              name: postgres-operator-secret-myapp02-active-pattern
+              key: values.yaml
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-ui-myapp02-active-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/values
+            value:
+              key1: value1
+              key2: value2
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-ui-myapp02-active-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: ConfigMap
+              name: postgres-operator-ui-cm-myapp02-active-pattern
+              key: values.yaml
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-ui-myapp02-active-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: Secret
+              name: postgres-operator-ui-secret-myapp02-active-pattern
+              key: values.yaml
+  - apiVersion: kustomize.toolkit.fluxcd.io/v1
+    kind: Kustomization
+    metadata:
+      name: ks-myapp02-active-pattern
+      namespace: flux-system
+      annotations:
+        config.kubernetes.io/index: '0'
+        config.kubernetes.io/path: ks-main.yaml
+        internal.config.kubernetes.io/index: '0'
+        internal.config.kubernetes.io/path: ks-main.yaml
+        internal.config.kubernetes.io/seqindent: compact
+    spec:
+      interval: 1h0m0s
+      path: apps/example2/manifests/main-pattern/main-brick-manifests
+      prune: true
+      sourceRef:
+        kind: GitRepository
+        name: sw-catalogs
+        namespace: flux-system
+      targetNamespace: app-namespace
+      postBuild:
+        substitute:
+          app_name: myapp02
+          example_parameter1: active-example-value1
+          example_parameter2: active-example-value2
+      wait: true
+  - apiVersion: v1
+    kind: ConfigMap
+    metadata:
+      name: postgres-operator-cm-myapp02-active-pattern
+      namespace: app-namespace
+      annotations:
+        config.kubernetes.io/path: postgres-operator-cm-myapp02-active-pattern.yaml
+        internal.config.kubernetes.io/path: postgres-operator-cm-myapp02-active-pattern.yaml
+    data:
+      values.yaml: |-
+        cm-key1: cm-value1
+        cm-key2: cm-value2
+  - apiVersion: v1
+    kind: Secret
+    metadata:
+      name: postgres-operator-secret-myapp02-active-pattern
+      namespace: app-namespace
+      annotations:
+        config.kubernetes.io/path: postgres-operator-secret-myapp02-active-pattern.yaml
+        internal.config.kubernetes.io/path: postgres-operator-secret-myapp02-active-pattern.yaml
+    data:
+      values.yaml: ENCRYPTED
+  - apiVersion: v1
+    kind: ConfigMap
+    metadata:
+      name: postgres-operator-ui-cm-myapp02-active-pattern
+      namespace: app-namespace
+      annotations:
+        config.kubernetes.io/path: postgres-operator-ui-cm-myapp02-active-pattern.yaml
+        internal.config.kubernetes.io/path: postgres-operator-ui-cm-myapp02-active-pattern.yaml
+    data:
+      values.yaml: |-
+        cm-key1: cm-value1
+        cm-key2: cm-value2
+  - apiVersion: kustomize.toolkit.fluxcd.io/v1
+    kind: Kustomization
+    metadata:
+      name: database-kustomization-myapp02-standby-pattern
+      namespace: flux-system
+      annotations:
+        config.kubernetes.io/index: '0'
+        config.kubernetes.io/path: ks-database.yaml
+        internal.config.kubernetes.io/index: '0'
+        internal.config.kubernetes.io/path: ks-database.yaml
+        internal.config.kubernetes.io/seqindent: compact
+    spec:
+      interval: 1h0m0s
+      path: apps/example2/manifests/main-pattern/database-manifests
+      prune: true
+      sourceRef:
+        kind: GitRepository
+        name: sw-catalogs
+        namespace: flux-system
+      targetNamespace: app-namespace
+      postBuild:
+        substitute:
+          appname: myapp02
+          appnamespace: app-namespace
+      wait: true
+      patches:
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-myapp02-standby-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/values
+            value:
+              key1: value1
+              key2: value2
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-myapp02-standby-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: ConfigMap
+              name: postgres-operator-cm-myapp02-standby-pattern
+              key: values.yaml
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-myapp02-standby-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: Secret
+              name: postgres-operator-secret-myapp02-standby-pattern
+              key: values.yaml
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-ui-myapp02-standby-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/values
+            value:
+              key1: value1
+              key2: value2
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-ui-myapp02-standby-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: ConfigMap
+              name: postgres-operator-ui-cm-myapp02-standby-pattern
+              key: values.yaml
+      - target:
+          kind: HelmRelease
+          name: postgres-operator-ui-myapp02-standby-pattern
+          namespace: app-namespace
+        patch: |
+          - op: add
+            path: /spec/valuesFrom/-
+            value:
+              kind: Secret
+              name: postgres-operator-ui-secret-myapp02-standby-pattern
+              key: values.yaml
+  - apiVersion: kustomize.toolkit.fluxcd.io/v1
+    kind: Kustomization
+    metadata:
+      name: ks-myapp02-standby-pattern
+      namespace: flux-system
+      annotations:
+        config.kubernetes.io/index: '0'
+        config.kubernetes.io/path: ks-main.yaml
+        internal.config.kubernetes.io/index: '0'
+        internal.config.kubernetes.io/path: ks-main.yaml
+        internal.config.kubernetes.io/seqindent: compact
+    spec:
+      interval: 1h0m0s
+      path: apps/example2/manifests/main-pattern/main-brick-manifests
+      prune: true
+      sourceRef:
+        kind: GitRepository
+        name: sw-catalogs
+        namespace: flux-system
+      targetNamespace: app-namespace
+      postBuild:
+        substitute:
+          app_name: myapp02
+          example_parameter1: standby-example-value1
+          example_parameter2: standby-example-value2
+      wait: true
+  - apiVersion: v1
+    kind: ConfigMap
+    metadata:
+      name: postgres-operator-cm-myapp02-standby-pattern
+      namespace: app-namespace
+      annotations:
+        config.kubernetes.io/path: postgres-operator-cm-myapp02-standby-pattern.yaml
+        internal.config.kubernetes.io/path: postgres-operator-cm-myapp02-standby-pattern.yaml
+    data:
+      values.yaml: |-
+        cm-key1: cm-value1
+        cm-key2: cm-value2
+  - apiVersion: v1
+    kind: Secret
+    metadata:
+      name: postgres-operator-secret-myapp02-standby-pattern
+      namespace: app-namespace
+      annotations:
+        config.kubernetes.io/path: postgres-operator-secret-myapp02-standby-pattern.yaml
+        internal.config.kubernetes.io/path: postgres-operator-secret-myapp02-standby-pattern.yaml
+    data:
+      values.yaml: ENCRYPTED
+  - apiVersion: v1
+    kind: ConfigMap
+    metadata:
+      name: postgres-operator-ui-cm-myapp02-standby-pattern
+      namespace: app-namespace
+      annotations:
+        config.kubernetes.io/path: postgres-operator-ui-cm-myapp02-standby-pattern.yaml
+        internal.config.kubernetes.io/path: postgres-operator-ui-cm-myapp02-standby-pattern.yaml
+    data:
+      values.yaml: |-
+        cm-key1: cm-value1
+        cm-key2: cm-value2
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/hrset-database.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/hrset-database.yaml
new file mode 100644
index 0000000..8623232
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/hrset-database.yaml
@@ -0,0 +1,58 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+---
+apiVersion: helm.toolkit.fluxcd.io/v2beta1
+kind: HelmRelease
+metadata:
+  name: postgres-operator-${appname}
+  namespace: ${appnamespace}
+spec:
+  chart:
+    spec:
+      chart: postgres-operator
+      reconcileStrategy: ChartVersion
+      sourceRef:
+        kind: HelmRepository
+        name: postgres-operator-charts-${appname}
+        namespace: ${appnamespace}
+  interval: 3m0s
+  targetNamespace: ${appnamespace}
+  values: {}
+
+---
+
+apiVersion: helm.toolkit.fluxcd.io/v2beta1
+kind: HelmRelease
+metadata:
+  name: postgres-operator-ui-${appname}
+  namespace: ${appnamespace}
+spec:
+  dependsOn:
+  - name: postgres-operator-${appname}
+    namespace: ${appnamespace}
+  chart:
+    spec:
+      chart: postgres-operator-ui
+      reconcileStrategy: ChartVersion
+      sourceRef:
+        kind: HelmRepository
+        name: postgres-operator-charts-${appname}
+        namespace: ${appnamespace}
+  interval: 3m0s
+  targetNamespace: ${appnamespace}
+  values: {}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml
new file mode 100644
index 0000000..eaf2dda
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/database-manifests/repo-zalando-postgres.yaml
@@ -0,0 +1,29 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+---
+apiVersion: source.toolkit.fluxcd.io/v1beta2
+kind: HelmRepository
+metadata:
+  name: postgres-operator-charts-${appname}
+  namespace: ${appnamespace}
+spec:
+  interval: 10m0s
+  # type: oci
+  # url: oci://registry-1.docker.io/bitnamicharts
+  type: default
+  url: https://opensource.zalando.com/postgres-operator/charts/postgres-operator
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/main-brick-manifests/configmap.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/main-brick-manifests/configmap.yaml
new file mode 100644
index 0000000..5bd5c50
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/manifests/main-pattern/main-brick-manifests/configmap.yaml
@@ -0,0 +1,23 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: my-cm-${appname}
+data:
+  my-key: my-value
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-database.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-database.yaml
new file mode 100644
index 0000000..6ece0e0
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-database.yaml
@@ -0,0 +1,36 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+  name: database-kustomization-${APPNAME}-${PATTERN_NAME}
+  namespace: flux-system
+spec:
+  interval: 1h0m0s
+  path: ./apps/example1/manifests/main-pattern/database-manifests
+  prune: true
+  sourceRef:
+    kind: GitRepository
+    name: sw-catalogs
+    namespace: flux-system
+  targetNamespace: ${APPNAMESPACE}
+  postBuild:
+    substitute:
+      appname: ${APPNAME}
+      appnamespace: ${APPNAMESPACE}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-main.yaml b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-main.yaml
new file mode 100644
index 0000000..7a39e0f
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/artifacts/sw-catalogs/apps/example2/templates/main-pattern/ks-main.yaml
@@ -0,0 +1,37 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+---
+apiVersion: kustomize.toolkit.fluxcd.io/v1
+kind: Kustomization
+metadata:
+  name: ks-${APPNAME}-${PATTERN_NAME}
+  namespace: flux-system
+spec:
+  interval: 1h0m0s
+  path: ./apps/example1/manifests/main-pattern/main-brick-manifests
+  prune: true
+  sourceRef:
+    kind: GitRepository
+    name: sw-catalogs
+    namespace: flux-system
+  targetNamespace: ${APPNAMESPACE}
+  postBuild:
+    substitute:
+      app_name: ${APPNAME}
+      example_parameter1: ${EXAMPLE_PARAMETER1}
+      example_parameter2: ${EXAMPLE_PARAMETER2}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/brick.nu b/docker/osm-nushell-krm-functions/operations/tests/brick.nu
new file mode 100644
index 0000000..602a24a
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/brick.nu
@@ -0,0 +1,18 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+# Module with custom functions to manage the transformations and generations associated to the different types of Building Blocks supported by OSM.
diff --git a/docker/osm-nushell-krm-functions/operations/tests/ksu.nu b/docker/osm-nushell-krm-functions/operations/tests/ksu.nu
new file mode 100644
index 0000000..3180566
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/ksu.nu
@@ -0,0 +1,19 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+# Module with custom functions to manage KSUs and their renderization of the corresponding ResourceList into a given target folder.
+
diff --git a/docker/osm-nushell-krm-functions/operations/tests/location.nu b/docker/osm-nushell-krm-functions/operations/tests/location.nu
new file mode 100644
index 0000000..6ea15b5
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/location.nu
@@ -0,0 +1,232 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+# Module with custom functions to manage KSUs and their renderization of the corresponding ResourceList into a given target folder.
+
+
+# Imports
+use std assert
+use ../location.nu *
+
+
+### to path components tests ###
+
+export def "test location to path components repo-path with base-path" [] {
+    let input: record = {
+        "repo-path": ["/custom/repo"],
+        "base-path": ["base/dir"],  # Now unused in base construction
+        "relative-path": ["file.txt"]
+    }
+    let actual: list<string> = ($input | to path components)
+    let expected: list<string> = [ "/custom/repo" "base/dir" "file.txt" ]
+    assert equal $actual $expected
+}
+
+
+export def "test location to path components repo-path with base-path with strings" [] {
+    let input: record = {
+        "repo-path": "/custom/repo",
+        "base-path": "base/dir",  # Now unused in base construction
+        "relative-path": "file.txt"
+    }
+    let actual: list<string> = ($input | to path components)
+    let expected: list<string> = [ "/custom/repo" "base/dir" "file.txt" ]
+    assert equal $actual $expected
+}
+
+
+export def "test location to path components explicit base-path usage" [] {
+    let input: record = {
+        "repo-name": "my_repo",
+        "base-path": "explicit/base",
+        "relative-path": "data.csv"
+    }
+    let actual: list<string> = ($input | to path components)
+    let expected: list<string> = [ "/repos/my_repo" "explicit/base" "data.csv" ]
+    assert equal $actual $expected
+}
+
+
+export def "test location to path components empty repo-name errors" [] {
+    let input: record = {
+        "repo-name": "",
+        "relative-path": "file.txt"
+    }
+    assert error { $input | to path components }
+}
+
+
+export def "test location to path components partial profile spec errors" [] {
+    let input: record = {
+        "repo-path": "/valid/repo",
+        "profile-type": "dev",
+        "relative-path": "file.txt"
+    }
+    assert error { $input | to path components }
+}
+
+
+export def "test location to path components mixed spec priorities" [] {
+    let input: record = {
+        "repo-path": "/primary/repo",
+        "repo-name": "secondary",
+        "base-path": "explicit_base",
+        "oka-type": "models",
+        "oka-name": "ai",
+        "relative-path": "config.yaml"
+    }
+    let actual: list<string> = ($input | to path components)
+    let expected: list<string> = [ "/primary/repo" "explicit_base" "config.yaml" ]
+    assert equal $actual $expected
+}
+
+
+# Updated existing tests with clearer names
+export def "test location to path components profile-based with normalization" [] {
+    let input: record = {
+        "repo-name": "profile_repo",
+        "profile-type": "PROD",
+        "profile-name": "EU_Cluster",
+        "relative-path": "secrets.env"
+    }
+    let actual: list<string> = ($input | to path components)
+    let expected: list<string> = [ "/repos/profile_repo" "osm_admin/PROD/EU_Cluster" "secrets.env" ]
+    assert equal $actual $expected
+}
+
+
+export def "test location to path components oka-based with normalization" [] {
+    let input: record = {
+        "repo-path": "/repos/oka_repo",
+        "oka-type": "DATA",
+        "oka-name": "Census2025",
+        "relative-path": "demographics.csv"
+    }
+    let actual: list<string> = ($input | to path components)
+    let expected: list<string> = [ "/repos/oka_repo" "DATA/Census2025" "demographics.csv" ]
+    assert equal $actual $expected
+}
+
+
+# TODO:
+
+### to absolute path tests ###
+
+export def "test location to absolute path basic repo-path" [] {
+    let input: record = {
+        "repo-path": ["/main/repo", "sw-catalogs"],
+        "base-path": ["apps", "example1"],
+        "relative-path": ["manifests", "main-pattern", "main-brick-manifests"]
+    }
+    let actual: string = ($input | to absolute path)
+    let expected: string = "/main/repo/sw-catalogs/apps/example1/manifests/main-pattern/main-brick-manifests"
+    assert equal $actual $expected
+}
+
+
+export def "test location to absolute path profile-based with defaults" [] {
+    let input: record = {
+        "repo-name": "fleet",
+        "profile-type": "dev",
+        "profile-name": "TestEnv",
+        "relative-path": ["app_instance01", "main"]
+    }
+    let actual = ($input | to absolute path)
+    let expected = "/repos/fleet/osm_admin/dev/TestEnv/app_instance01/main"
+    assert equal $actual $expected
+}
+
+
+export def "test location to absolute path oka-based with custom defaults" [] {
+    let input: record = {
+        "repo-name": "data_repo",
+        "oka-type": "app",  # It should be converted to "apps"
+        "oka-name": "upf",
+        "relative-path": ["2024", "main"]
+    }
+    let actual: string = ($input | to absolute path "geo" "/data")
+    let expected: string = "/data/data_repo/apps/upf/2024/main"
+    assert equal $actual $expected
+}
+
+
+export def "test location to absolute path mixed specifications priority" [] {
+    let input: record = {
+        "repo-name": "fleet",
+        "base-path": ["my_oka"],
+        "relative-path": ["manifests"]
+    }
+    let actual: string = ($input | to absolute path)
+    let expected: string = "/repos/fleet/my_oka/manifests"
+    assert equal $actual $expected
+}
+
+
+export def "test location to absolute path special characters handling" [] {
+    let input: record = {
+        "repo-name": "fleet",
+        "profile-type": "apps",     # Should become "app-profiles"
+        "profile-name": "mycluster01",
+        "relative-path": ["configs/prod"]
+    }
+    let actual: string = ($input | to absolute path)
+    let expected: string = "/repos/fleet/osm_admin/app-profiles/mycluster01/configs/prod"
+    assert equal $actual $expected
+}
+
+
+export def "test location to absolute path error missing relative-path" [] {
+    let input: record = {
+        "repo-path": ["/valid/repo"],
+        "base-path": ["valid/base"]
+    }
+    assert error { $input | to absolute path }
+}
+
+
+export def "test location to absolute path nested relative path" [] {
+    let input: record = {
+        "repo-path": ["/repos/core"],
+        "oka-type": "infra-controllers",
+        "oka-name": "predictive",
+        "relative-path": ["mobile", "serverless-web"]
+    }
+    let actual: string = ($input | to absolute path)
+    let expected: string = "/repos/core/infra-controllers/predictive/mobile/serverless-web"
+    assert equal $actual $expected
+}
+
+
+export def "test location to absolute path empty repo-name error" [] {
+    let input: record = {
+        "repo-name": "",
+        "relative-path": ["file.txt"]
+    }
+    assert error { $input | to absolute path }
+}
+
+
+export def "test location to absolute path minimal valid input" [] {
+    let input: record = {
+        "repo-name": "fleet",
+        "base-path": ["apps"],
+        "relative-path": ["test-app"]
+    }
+    let actual: string = ($input | to absolute path)
+    let expected: string = "/repos/fleet/apps/test-app"
+    assert equal $actual $expected
+}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/mod.nu b/docker/osm-nushell-krm-functions/operations/tests/mod.nu
new file mode 100644
index 0000000..21f40d6
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/mod.nu
@@ -0,0 +1,55 @@
+#!/usr/bin/env -S nu --stdin
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+
+use std assert
+# use ../../krm *
+
+use ./location.nu *
+use ./app.nu *
+# use ./ksu.nu *
+# use ./pattern.nu *
+# use ./brick.nu *
+
+
+# Test launcher
+def main [] {
+    print "Running tests..."
+
+    let test_commands: list<string> = (
+        scope commands
+            | where ($it.type == "custom")
+                and ($it.name | str starts-with "test ")
+                and not ($it.description | str starts-with "ignore")
+            | get name
+    )
+
+    let count_test_commands: int = ($test_commands | length)
+    let test_commands_together: string = (
+        $test_commands
+        | enumerate
+        | each { |test|
+            [$"print '--> [($test.index + 1)/($count_test_commands)] ($test.item)'", $test.item]
+        }
+        | flatten
+        | str join ";"
+    )
+
+    nu --commands $"source `($env.CURRENT_FILE)`; ($test_commands_together)"
+    print $"\n✅ ALL TESTS COMPLETED SUCCESSFULLY"
+}
diff --git a/docker/osm-nushell-krm-functions/operations/tests/pattern.nu b/docker/osm-nushell-krm-functions/operations/tests/pattern.nu
new file mode 100644
index 0000000..68acc80
--- /dev/null
+++ b/docker/osm-nushell-krm-functions/operations/tests/pattern.nu
@@ -0,0 +1,18 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+
+# Module with custom functions to manage a Pattern definition, taking into account its corresponding source template and the set of transformations specified for its constituent Bricks.