require 'helix_web_services_client' require 'uri' require 'P4' require_relative '../../helix_web_services/lib/hws_strings' #------------------------------------------------------------------------------ # Configuration #------------------------------------------------------------------------------ WS_URL = URI(ENV['WS_URL'] || 'http://localhost:9000/') # This is used to prepare a shelved changelist for the 'submit_change' method P4PORT = ENV['P4PORT'] || 'localhost:1666' TMP_FOLDER = '/tmp/clients' DELETE_CLIENT = !ENV.key?('KEEP_WORKSPACES') CHARSET = ENV['P4CHARSET'] || 'auto' JDOE_USER = 'jdoe' JDOE_PASSWORD = 'johndoe1A!' JDOE_USER2 = 'j\doe' JDOE_PASSWORD2 = 'johndoe1A!' SUPER_USER = 'super' SUPER_PASSWORD = 'superuser1A!' DEVICE_SYNC = File.absolute_path('../../../contrib/deviceSync', __FILE__) #------------------------------------------------------------------------------ # Helpers #------------------------------------------------------------------------------ def cloud_test? (ENV['CLOUD_TEST'] || 'false') =~ (/^(true|1|t|yes$)/i) end def client_as_jdoe client = HelixWebServicesClient.new( url: WS_URL, user: 'jdoe', password: 'johndoe1A!', ssl: {verify: false} ) # also set the P4PORT and P4PASSWD headers client.add_setting('P4PORT',P4PORT) client.add_setting('P4PASSWD',client.login(JDOE_USER, JDOE_PASSWORD)) yield client ensure client.close if client end def client_as_super client = HelixWebServicesClient.new( url: WS_URL, user: 'super', password: 'superuser1A!', ssl: {verify: false} ) # also set the P4PORT and P4PASSWD headers client.add_setting('P4PORT',P4PORT) client.add_setting('P4PASSWD',client.login(SUPER_USER, SUPER_PASSWORD)) yield client ensure client.close if client end def client_as_jdoe2 client = HelixWebServicesClient.new( url: WS_URL, user: 'j\doe', password: 'jdoejdoe1A!', ssl: {verify: false} ) yield client ensure client.close if client end def p4_as_jdoe p4 = P4.new # Make P4Ruby only raise exceptions if there are errors. Warnings # (such as 'no such file(s)' don't get the same treatment. p4.exception_level = P4::RAISE_ERRORS p4.port = P4PORT p4.user = JDOE_USER p4.password = JDOE_PASSWORD p4.charset = CHARSET unless CHARSET.nil? || CHARSET == 'none' p4.connect results = p4.run_login('-p') p4.password = results.first yield p4 if block_given? return p4 ensure p4.disconnect if block_given? end def temp_client_as_jdoe(&block) p4_as_jdoe do |p4| unless Dir.exists?(TMP_FOLDER) FileUtils.makedirs(TMP_FOLDER) end name = (0...8).map { (65 + rand(26)).chr }.join root = File.join(TMP_FOLDER, name) Dir.mkdir(root) p4.client = name spec = p4.fetch_client spec._root = root spec._client = name spec._description = 'test client' # When there's one depot, the view generated maps //depot/... //view/... # instead of //depot/... //view/depot/... like it will with multiple depots. if (spec._view.length == 1) spec._view = ["//depot/... //#{name}/depot/..."] end p4.save_client(spec) p4.run_sync('//...') block.call(p4, root) if DELETE_CLIENT p4.user = SUPER_USER p4.password = SUPER_PASSWORD results = p4.run_login('-p') p4.password = results.first p4.run_client('-d', '-f', name) FileUtils.rmtree(root) end end end def p4_as_super p4 = P4.new # Make P4Ruby only raise exceptions if there are errors. Warnings # (such as 'no such file(s)' don't get the same treatment. p4.exception_level = P4::RAISE_ERRORS p4.port = P4PORT p4.user = SUPER_USER p4.password = SUPER_PASSWORD p4.charset = CHARSET unless CHARSET.nil? || CHARSET == 'none' p4.connect results = p4.run_login('-p') p4.password = results.first yield p4 if block_given? return p4 ensure p4.disconnect if block_given? end def temp_client_as_super(&block) p4_as_super do |p4| unless Dir.exists?(TMP_FOLDER) FileUtils.makedirs(TMP_FOLDER) end name = (0...8).map { (65 + rand(26)).chr }.join root = File.join(TMP_FOLDER, name) Dir.mkdir(root) p4.client = name spec = p4.fetch_client spec._root = root spec._client = name spec._description = 'test client' # When there's one depot, the view generated maps //depot/... //view/... # instead of //depot/... //view/depot/... like it will with multiple depots. if (spec._view.length == 1) spec._view = ["//depot/... //#{name}/depot/..."] end p4.save_client(spec) p4.run_sync('//...') keep_client = block.call(p4, root) if keep_client != true && DELETE_CLIENT p4.user = SUPER_USER p4.password = SUPER_PASSWORD results = p4.run_login('-p') p4.password = results.first p4.run_client('-d', '-f', name) FileUtils.rmtree(root) end end end def temp_stream_client_as_jdoe(&block) p4_as_jdoe do |p4| name = (0...8).map { (65 + rand(26)).chr }.join # Create a stream in the stream-test depot stream_spec = p4.fetch_stream("//stream-test/main-#{name}") stream_spec._type = 'mainline' stream_spec._parent = 'none' p4.save_stream(stream_spec) unless Dir.exists?(TMP_FOLDER) FileUtils.makedirs(TMP_FOLDER) end root = File.join(TMP_FOLDER, name) Dir.mkdir(root) p4.client = name spec = p4.fetch_client spec._root = root spec._client = name spec._description = 'test client' spec._view = nil spec._stream = "//stream-test/main-#{name}" p4.save_client(spec) p4.run_sync('//...') block.call(p4, root, "//stream-test/main-#{name}") if DELETE_CLIENT p4.user = SUPER_USER p4.password = SUPER_PASSWORD results = p4.run_login('-p') p4.password = results.first p4.run_client('-d', '-f', name) FileUtils.rmtree(root) end end end def temp_stream_client_as_super(project, &block) p4_as_super do |p4| name = (0...8).map { (65 + rand(26)).chr }.join # Create a stream in the stream-test depot stream_spec = p4.fetch_stream("//#{project}/main") stream_spec._type = 'mainline' stream_spec._parent = 'none' p4.save_stream(stream_spec) unless Dir.exists?(TMP_FOLDER) FileUtils.makedirs(TMP_FOLDER) end root = File.join(TMP_FOLDER, name) Dir.mkdir(root) p4.client = name spec = p4.fetch_client spec._root = root spec._client = name spec._description = 'test client' spec._view = nil spec._stream = "//#{project}/main" p4.save_client(spec) p4.run_sync('//...') keep_client = block.call(p4, root, "//#{project}/main") if keep_client != true && DELETE_CLIENT p4.user = SUPER_USER p4.password = SUPER_PASSWORD results = p4.run_login('-p') p4.password = results.first p4.run_client('-d', '-f', name) FileUtils.rmtree(root) end end end def depot_path_from_name(project_name) cloud_test? ? "//#{project_name}/main" : "//depot/main/#{project_name}" end def root_depot_path_from_name(project_name) cloud_test? ? depot_path_from_name(project_name) : "//depot/main" end def create_cloud_project(project_name) # don't need to do anything for local tests return unless cloud_test? p4_as_super do |p4s| # TODO: don't do it if it already exists # create a depot depot = p4s.fetch_depot(project_name) depot._type = 'stream' p4s.save_depot(depot) stream = p4s.fetch_stream('-t','mainline',"//#{project_name}/main") stream._name = project_name p4s.save_stream(stream) end end def create_stream_client(project, name) # make a client as well p4_as_super do |p4s| client = p4s.fetch_client('-S',"//#{project}/main",name) dir = File.join('/tmp/sync_test/', name) if Dir.exist?(dir) FileUtils.rm_r(dir) end if !Dir.exist?(dir) FileUtils.mkpath(dir) end client._root = dir p4s.save_client(client) return dir end end def p4ticket_from_client(client) client.settings['P4PASSWD'] end # This will create a local "helix sync" client against the project server. # # In general, the depot_path should be underneath the global HVE_PROJECTS_PATH # setting, which should be set for any operation you need. # # @param depot_path Where the Helix Sync project should be located in the p4d instance # @param client HelixWebServicesClient instance # @param p4 The p4 connection we'll use for local operations, will be reconfigured # @return The local directory we've established as the sync client def create_sync_project(project_name, client, p4) depot_path = depot_path_from_name(project_name) root_depot_path = root_depot_path_from_name(project_name) project_id = HWSStrings.component_encode(project_name) root_dir = Dir.mktmpdir('sync_test') client_info = client.create_helix_sync_device_client(project_id, 'test-machine', root_dir) create_p4config(root_dir, client.user, p4ticket_from_client(client), client_info.client) p4.client = client_info.client sync_output = p4.run_sync("#{depot_path}/...") puts("sync_output #{sync_output}") [root_dir, client_info.client, depot_path, root_depot_path] end def create_p4config(root_dir, user, ticket, client_name) p4config_path = File.join(root_dir, '.p4config') puts "Creating .p4config file in #{root_dir}" IO.write(p4config_path, <<-END.gsub(/^[ ]{8}/, '') P4PORT=#{P4PORT} P4CLIENT=#{client_name} P4PASSWD=#{ticket} P4USER=#{user} END ) end # Launches the device sync application which should set up the pending changelist def run_device_sync(project_name, root_dir, client, device_client) project_id = HWSStrings.component_encode(project_name) client_info = client.create_helix_sync_shelf_client(project_id) shelf_client = client_info.client puts "Client info is #{client_info.inspect}, shelf info is #{shelf_client.inspect}" # Run with SHELF_CLIENT in root direcotry Dir.chdir(root_dir) { ok = system("P4CONFIG=.p4config SHELF_CLIENT=#{shelf_client} P4CLIENT=#{device_client} #{DEVICE_SYNC}") fail 'deviceSync failed' unless ok } end def upload_test_file(project, file_path, content) if !cloud_test? # use the /p4/... api for classic depots client_as_super do |cs| cs.upload_file({DepotFile: depot_path_from_name(project) + '/' + file_path, 'Content': content}) end else dir = create_stream_client(project, "upload_test_file_#{project}") path = File.join(dir, file_path) IO.write(path, content) p4_as_super do |p4s| p4s.client = "upload_test_file_#{project}" p4s.run_add(path) p4s.run_submit('-d', "Upload test file for #{project} : #{file_path}") end end end def delete_test_file(project, file) if !cloud_test? temp_client_as_super do |p4s| r = p4s.run_delete("#{depot_path_from_name(project)}/#{file}") puts "remove from shelf delete: #{r}" r = p4s.run_submit('-d', "deleting #{file}") puts "remove from shelf submit: #{r}" end else temp_stream_client_as_super(project) do |p4s| r = p4s.run_delete("#{depot_path_from_name(project)}/#{file}") puts "remove from shelf delete: #{r}" r = p4s.run_submit('-d', "deleting #{file}") puts "remove from shelf submit: #{r}" end end end def edit_and_lock(project, file) if !cloud_test? temp_client_as_super do |p4s| edit_results = p4s.run_edit("#{depot_path_from_name(project)}/#{file}") puts "edit_results #{edit_results}" lock_results = p4s.run_lock("#{depot_path_from_name(project)}/#{file}") puts "lock_results #{lock_results}" true end else temp_stream_client_as_super(project) do |p4s| edit_results = p4s.run_edit("#{depot_path_from_name(project)}/#{file}") puts "edit_results #{edit_results}" lock_results = p4s.run_lock("#{depot_path_from_name(project)}/#{file}") puts "lock_results #{lock_results}" true end end end def create_new_file_under(dir) randstr = (0...8).map { (65 + rand(26)).chr }.join file_name = "test_#{randstr}" path = File.join(dir, file_name) IO.write(path, randstr) file_name end
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#10 | 16274 | Doug Scheirer | a little code cleanup, no functional changes | ||
#9 | 16271 | Doug Scheirer |
- delete the default //depot for cloud tests - mark more tests as pending - refactored sync tests a little to make cloud compatib;e - fixed a bug in the sync 'locked' logic |
||
#8 | 16114 | Doug Scheirer | Merge from main | ||
#7 | 16040 | Doug Scheirer | I think the pending tests is at a minimum for cloud enabled, so readyto integrate up? | ||
#6 | 16026 | Doug Scheirer | Marked last few cloud tests pending, enabled test:cloud by default | ||
#5 | 16024 | Doug Scheirer | Some cloud spec forking, got the mock_raymond to spin up in cloud testing, split normal vs cloud spec output | ||
#4 | 16014 | Doug Scheirer | Merge down from main | ||
#3 | 15868 | Doug Scheirer | Merge from main | ||
#2 | 15845 | Doug Scheirer | Integ from main | ||
#1 | 15688 | Doug Scheirer |
Populate -o //guest/perforce_software/helix-web-services/... //guest/doug_scheirer/helix-web-services/.... |
||
//guest/perforce_software/helix-web-services/main/source/helix_web_services_client/spec/test_config.rb | |||||
#1 | 15622 | tjuricek |
Move source code to 'source/' subdirectory of branch. build/ will remain where it is. |
||
//guest/perforce_software/helix-web-services/main/helix_web_services_client/spec/test_config.rb | |||||
#5 | 15147 | tjuricek |
Enable submissions by files on stream clients This obeys our earlier rules: notably the local client must not have any local files open for edit. |
||
#4 | 15142 | tjuricek |
Use the super permission to clean up clients used in tests. For whatever reason, when I was testing this using a localhost connection, I never ran into this issue. Might be special rules to avoid permission checks locally? |
||
#3 | 15133 | tjuricek | Fix case to require 'P4' statement: "require 'p4'" only works on a case-insensitive system. | ||
#2 | 15132 | tjuricek | Provde a basic submit -e mechanism on classic perforce workspaces. | ||
#1 | 15059 | tjuricek | Tested authentication of the core /auth/v1/login method. | ||
//guest/perforce_software/helix-web-services/main/helix_web_services_client/spec/test_connections.rb | |||||
#4 | 15053 | tjuricek |
Revise the client API to use the new login method. The current specs will need to be revised since data normalization is moving out of the server and into the client. |
||
#3 | 14899 | tjuricek | Do not verify SSL certs in tests, and make sure the right DB file is used by our startup script. | ||
#2 | 14898 | tjuricek |
Specify the entire host string when executing tests against a remote host. The new package sets up vs a self-signed cert, so now we can watch failures over SSL. |
||
#1 | 13799 | tjuricek |
Start with branch specs hosting in a new monolithic 'helix web services' project. Converting from a microservice to a monolithic architecture due to resource constraints at getting a deployable system running. Additionally, since it's not expected that people will upgrade often, the major benefit of microservices - being able to add services individually without affecting others - is not really a major benefit. The Ruby SDK will be consolidated into a single 'helix web services client' project. It may end up being distributed via Rubygems. This only runs branch specs at the moment. I want to get a CD pipeline setup for the monolithic server before revising more methods. |