package npm import ( "io" "path/filepath" "regexp" "testing" "github.com/SAP/jenkins-library/pkg/piperutils" "github.com/stretchr/testify/assert" ) func TestNpmPublish(t *testing.T) { type wants struct { publishConfigPath string publishConfig string tarballPath string err string } tt := []struct { name string files map[string]string packageDescriptors []string registryURL string registryUser string registryPassword string packBeforePublish bool wants wants }{ // project in root folder { name: "success - single project, publish normal, unpacked package - target registry in npmrc", files: map[string]string{ "package.json": `{"name": "piper-project", "version": "0.0.1"}`, ".piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"package.json"}, wants: wants{ publishConfigPath: `\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, }, { name: "success - single project, publish normal, unpacked package - target registry from pipeline", files: map[string]string{ "package.json": `{"name": "piper-project", "version": "0.0.1"}`, }, packageDescriptors: []string{"package.json"}, registryURL: "https://my.private.npm.registry/", registryUser: "ThisIsTheUser", registryPassword: "AndHereIsThePassword", wants: wants{ publishConfigPath: `\.piperNpmrc`, publishConfig: "registry=https://my.private.npm.registry/\n_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nalways-auth=true\n", }, }, { name: "success - single project, publish normal, unpacked package - target registry from pipeline (precedence over npmrc)", files: map[string]string{ "package.json": `{"name": "piper-project", "version": "0.0.1"}`, ".piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"package.json"}, registryURL: "https://my.other.private.npm.registry/", registryUser: "ThisIsTheOtherUser", registryPassword: "AndHereIsTheOtherPassword", wants: wants{ publishConfigPath: `\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlT3RoZXJVc2VyOkFuZEhlcmVJc1RoZU90aGVyUGFzc3dvcmQ=\nregistry=https://my.other.private.npm.registry/\nalways-auth=true\n", }, }, { name: "success - single project, publish normal, packed - target registry in npmrc", files: map[string]string{ "package.json": `{"name": "piper-project", "version": "0.0.1"}`, ".piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"package.json"}, packBeforePublish: true, wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", tarballPath: "/temp-test/package.tgz", }, }, { name: "success - single project, publish normal, packed - target registry from pipeline", files: map[string]string{ "package.json": `{"name": "piper-project", "version": "0.0.1"}`, }, packageDescriptors: []string{"package.json"}, packBeforePublish: true, registryURL: "https://my.private.npm.registry/", registryUser: "ThisIsTheUser", registryPassword: "AndHereIsThePassword", wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "registry=https://my.private.npm.registry/\n_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nalways-auth=true\n", tarballPath: "/temp-test/package.tgz", }, }, { name: "success - single project, publish normal, packed - target registry from pipeline (precedence over npmrc)", files: map[string]string{ "package.json": `{"name": "piper-project", "version": "0.0.1"}`, ".piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"package.json"}, packBeforePublish: true, registryURL: "https://my.other.private.npm.registry/", registryUser: "ThisIsTheOtherUser", registryPassword: "AndHereIsTheOtherPassword", wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlT3RoZXJVc2VyOkFuZEhlcmVJc1RoZU90aGVyUGFzc3dvcmQ=\nregistry=https://my.other.private.npm.registry/\nalways-auth=true\n", tarballPath: "/temp-test/package.tgz", }, }, // scoped project { name: "success - single project, publish scoped, unpacked package - target registry in npmrc", files: map[string]string{ "package.json": `{"name": "@piper/project", "version": "0.0.1"}`, ".piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n@piper:registry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"package.json"}, wants: wants{ publishConfigPath: `\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n@piper:registry=https://my.private.npm.registry/\n", }, }, { name: "success - single project, publish scoped, unpacked package - target registry from pipeline", files: map[string]string{ "package.json": `{"name": "@piper/project", "version": "0.0.1"}`, }, packageDescriptors: []string{"package.json"}, registryURL: "https://my.private.npm.registry/", registryUser: "ThisIsTheUser", registryPassword: "AndHereIsThePassword", wants: wants{ publishConfigPath: `\.piperNpmrc`, publishConfig: "registry=https://my.private.npm.registry/\n@piper:registry=https://my.private.npm.registry/\n_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nalways-auth=true\n", }, }, { name: "success - single project, publish scoped, unpacked package - target registry from pipeline (precedence over npmrc)", files: map[string]string{ "package.json": `{"name": "@piper/project", "version": "0.0.1"}`, ".piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"package.json"}, registryURL: "https://my.other.private.npm.registry/", registryUser: "ThisIsTheOtherUser", registryPassword: "AndHereIsTheOtherPassword", wants: wants{ publishConfigPath: `\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlT3RoZXJVc2VyOkFuZEhlcmVJc1RoZU90aGVyUGFzc3dvcmQ=\nregistry=https://my.other.private.npm.registry/\n@piper:registry=https://my.other.private.npm.registry/\nalways-auth=true\n", }, }, { name: "success - single project, publish scoped, packed - target registry in npmrc", files: map[string]string{ "package.json": `{"name": "@piper/project", "version": "0.0.1"}`, ".piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\n@piper:registry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"package.json"}, packBeforePublish: true, wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\n@piper:registry=https://my.private.npm.registry/\n", tarballPath: "/temp-test/package.tgz", }, }, { name: "success - single project, publish scoped, packed - target registry from pipeline", files: map[string]string{ "package.json": `{"name": "@piper/project", "version": "0.0.1"}`, }, packageDescriptors: []string{"package.json"}, packBeforePublish: true, registryURL: "https://my.private.npm.registry/", registryUser: "ThisIsTheUser", registryPassword: "AndHereIsThePassword", wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "registry=https://my.private.npm.registry/\n@piper:registry=https://my.private.npm.registry/\n_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nalways-auth=true\n", tarballPath: "/temp-test/package.tgz", }, }, { name: "success - single project, publish scoped, packed - target registry from pipeline (precedence over npmrc)", files: map[string]string{ "package.json": `{"name": "@piper/project", "version": "0.0.1"}`, ".piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"package.json"}, packBeforePublish: true, registryURL: "https://my.other.private.npm.registry/", registryUser: "ThisIsTheOtherUser", registryPassword: "AndHereIsTheOtherPassword", wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlT3RoZXJVc2VyOkFuZEhlcmVJc1RoZU90aGVyUGFzc3dvcmQ=\nregistry=https://my.other.private.npm.registry/\n@piper:registry=https://my.other.private.npm.registry/\nalways-auth=true\n", tarballPath: "/temp-test/package.tgz", }, }, // project in a subfolder { name: "success - single project in subfolder, publish normal, unpacked package - target registry in npmrc", files: map[string]string{ "sub/package.json": `{"name": "piper-project", "version": "0.0.1"}`, "sub/.piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"sub/package.json"}, wants: wants{ publishConfigPath: `sub/\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, }, { name: "success - single project in subfolder, publish normal, unpacked package - target registry from pipeline", files: map[string]string{ "sub/package.json": `{"name": "piper-project", "version": "0.0.1"}`, }, packageDescriptors: []string{"sub/package.json"}, registryURL: "https://my.private.npm.registry/", registryUser: "ThisIsTheUser", registryPassword: "AndHereIsThePassword", wants: wants{ publishConfigPath: `sub/\.piperNpmrc`, publishConfig: "registry=https://my.private.npm.registry/\n_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nalways-auth=true\n", }, }, { name: "success - single project in subfolder, publish normal, unpacked package - target registry from pipeline (precedence over npmrc)", files: map[string]string{ "sub/package.json": `{"name": "piper-project", "version": "0.0.1"}`, "sub/.piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"sub/package.json"}, registryURL: "https://my.other.private.npm.registry/", registryUser: "ThisIsTheOtherUser", registryPassword: "AndHereIsTheOtherPassword", wants: wants{ publishConfigPath: `sub/\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlT3RoZXJVc2VyOkFuZEhlcmVJc1RoZU90aGVyUGFzc3dvcmQ=\nregistry=https://my.other.private.npm.registry/\nalways-auth=true\n", }, }, { name: "success - single project in subfolder, publish normal, packed - target registry in npmrc", files: map[string]string{ "sub/package.json": `{"name": "piper-project", "version": "0.0.1"}`, "sub/.piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"sub/package.json"}, packBeforePublish: true, wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", tarballPath: "/temp-test/package.tgz", }, }, { name: "success - single project in subfolder, publish normal, packed - target registry from pipeline", files: map[string]string{ "sub/package.json": `{"name": "piper-project", "version": "0.0.1"}`, }, packageDescriptors: []string{"sub/package.json"}, packBeforePublish: true, registryURL: "https://my.private.npm.registry/", registryUser: "ThisIsTheUser", registryPassword: "AndHereIsThePassword", wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "registry=https://my.private.npm.registry/\n_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nalways-auth=true\n", tarballPath: "/temp-test/package.tgz", }, }, { name: "success - single project in subfolder, publish normal, packed - target registry from pipeline (precedence over npmrc)", files: map[string]string{ "sub/package.json": `{"name": "piper-project", "version": "0.0.1"}`, "sub/.piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"sub/package.json"}, packBeforePublish: true, registryURL: "https://my.other.private.npm.registry/", registryUser: "ThisIsTheOtherUser", registryPassword: "AndHereIsTheOtherPassword", wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlT3RoZXJVc2VyOkFuZEhlcmVJc1RoZU90aGVyUGFzc3dvcmQ=\nregistry=https://my.other.private.npm.registry/\nalways-auth=true\n", tarballPath: "/temp-test/package.tgz", }, }, // scoped project in a subfolder { name: "success - single project in subfolder, publish scoped, unpacked package - target registry in npmrc", files: map[string]string{ "sub/package.json": `{"name": "@piper/project", "version": "0.0.1"}`, "sub/.piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n@piper:registry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"sub/package.json"}, wants: wants{ publishConfigPath: `\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n@piper:registry=https://my.private.npm.registry/\n", }, }, { name: "success - single project in subfolder, publish scoped, unpacked package - target registry from pipeline", files: map[string]string{ "sub/package.json": `{"name": "@piper/project", "version": "0.0.1"}`, }, packageDescriptors: []string{"sub/package.json"}, registryURL: "https://my.private.npm.registry/", registryUser: "ThisIsTheUser", registryPassword: "AndHereIsThePassword", wants: wants{ publishConfigPath: `\.piperNpmrc`, publishConfig: "registry=https://my.private.npm.registry/\n@piper:registry=https://my.private.npm.registry/\n_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nalways-auth=true\n", }, }, { name: "success - single project in subfolder, publish scoped, unpacked package - target registry from pipeline (precedence over npmrc)", files: map[string]string{ "sub/package.json": `{"name": "@piper/project", "version": "0.0.1"}`, "sub/.piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"sub/package.json"}, registryURL: "https://my.other.private.npm.registry/", registryUser: "ThisIsTheOtherUser", registryPassword: "AndHereIsTheOtherPassword", wants: wants{ publishConfigPath: `\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlT3RoZXJVc2VyOkFuZEhlcmVJc1RoZU90aGVyUGFzc3dvcmQ=\nregistry=https://my.other.private.npm.registry/\n@piper:registry=https://my.other.private.npm.registry/\nalways-auth=true\n", }, }, { name: "success - single project in subfolder, publish scoped, packed - target registry in npmrc", files: map[string]string{ "sub/package.json": `{"name": "@piper/project", "version": "0.0.1"}`, "sub/.piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\n@piper:registry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"sub/package.json"}, packBeforePublish: true, wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\n@piper:registry=https://my.private.npm.registry/\n", tarballPath: "/temp-test/package.tgz", }, }, { name: "success - single project in subfolder, publish scoped, packed - target registry from pipeline", files: map[string]string{ "sub/package.json": `{"name": "@piper/project", "version": "0.0.1"}`, }, packageDescriptors: []string{"sub/package.json"}, packBeforePublish: true, registryURL: "https://my.private.npm.registry/", registryUser: "ThisIsTheUser", registryPassword: "AndHereIsThePassword", wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "registry=https://my.private.npm.registry/\n@piper:registry=https://my.private.npm.registry/\n_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nalways-auth=true\n", tarballPath: "/temp-test/package.tgz", }, }, { name: "success - single project in subfolder, publish scoped, packed - target registry from pipeline (precedence over npmrc)", files: map[string]string{ "sub/package.json": `{"name": "@piper/project", "version": "0.0.1"}`, "sub/.piperNpmrc": "_auth=VGhpc0lzVGhlVXNlcjpBbmRIZXJlSXNUaGVQYXNzd29yZA==\nregistry=https://my.private.npm.registry/\n", }, packageDescriptors: []string{"sub/package.json"}, packBeforePublish: true, registryURL: "https://my.other.private.npm.registry/", registryUser: "ThisIsTheOtherUser", registryPassword: "AndHereIsTheOtherPassword", wants: wants{ publishConfigPath: `temp-(?:test|[0-9]+)/\.piperNpmrc`, publishConfig: "_auth=VGhpc0lzVGhlT3RoZXJVc2VyOkFuZEhlcmVJc1RoZU90aGVyUGFzc3dvcmQ=\nregistry=https://my.other.private.npm.registry/\n@piper:registry=https://my.other.private.npm.registry/\nalways-auth=true\n", tarballPath: "/temp-test/package.tgz", }, }, // TODO multiple projects } for _, test := range tt { t.Run(test.name, func(t *testing.T) { utils := newNpmMockUtilsBundle() for path, content := range test.files { utils.AddFile(path, []byte(content)) } options := ExecutorOptions{} exec := &Execute{ Utils: &utils, Options: options, } propertiesLoadFile = utils.FileRead propertiesWriteFile = utils.FileWrite writeIgnoreFile = utils.FileWrite // This stub simulates the behavior of npm pack and puts a tgz into the requested utils.execRunner.Stub = func(call string, stdoutReturn map[string]string, shouldFailOnCommand map[string]error, stdout io.Writer) error { r := regexp.MustCompile(`npm\s+pack\s+.*--pack-destination\s+(?P[^\s]+).*`) matches := r.FindStringSubmatch(call) if len(matches) == 0 { return nil } packDestination := matches[1] utils.AddFile(filepath.Join(packDestination, "package.tgz"), []byte("this is a tgz file")) return nil } err := exec.PublishAllPackages(test.packageDescriptors, test.registryURL, test.registryUser, test.registryPassword, test.packBeforePublish) if len(test.wants.err) == 0 && assert.NoError(t, err) { if assert.NotEmpty(t, utils.execRunner.Calls) { // last call is expected to be npm publish publishCmd := utils.execRunner.Calls[len(utils.execRunner.Calls)-1] assert.Equal(t, "npm", publishCmd.Exec) assert.Equal(t, "publish", publishCmd.Params[0]) if len(test.wants.tarballPath) > 0 && assert.Contains(t, publishCmd.Params, "--tarball") { tarballPath := publishCmd.Params[piperutils.FindString(publishCmd.Params, "--tarball")+1] assert.Equal(t, test.wants.tarballPath, tarballPath) } if assert.Contains(t, publishCmd.Params, "--userconfig") { effectivePublishConfigPath := publishCmd.Params[piperutils.FindString(publishCmd.Params, "--userconfig")+1] assert.Regexp(t, test.wants.publishConfigPath, effectivePublishConfigPath) effectiveConfig, err := utils.FileRead(effectivePublishConfigPath) if assert.NoError(t, err) { assert.Equal(t, test.wants.publishConfig, string(effectiveConfig)) } } } } else { assert.EqualError(t, err, test.wants.err) } }) } }