outofcoffee / imposter

Scriptable, multipurpose mock server. Run standalone mock servers, or embed mocks within your tests.
https://imposter.sh
Other
362 stars 59 forks source link

Issues with SOAP mocking #597

Open rdruilhe opened 2 months ago

rdruilhe commented 2 months ago

Hello,

I am trying to mimic a SOAP server from a third party organization so that we are able to work without depending on the remote server. We do not have access to the remote server source code, we only have the WSDL file and a snippet as an example to use it.

Here is the WSDL file of the SOAP services I would like to mock.

<?xml version="1.0"?>
<definitions xmlns:tns="https://third-party-organization.com/app/out/soap.php"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
             name="soapInterface"
             targetNamespace="https://third-party-organization.com/app/out/soap.php">
  <wsdl:types>
    <xsd:schema targetNamespace="https://third-party-organization.com/app/out/soap.php">
      <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
      <xsd:complexType name="tpo_connexion">
        <xsd:all>
          <xsd:element name="user" type="xsd:string"/>
          <xsd:element name="password" type="xsd:string"/>
        </xsd:all>
      </xsd:complexType>
      <xsd:complexType name="tpo_verify_member">
        <xsd:all>
          <xsd:element name="exist" type="xsd:int"/>
          <xsd:element name="id" type="xsd:string"/>
          <xsd:element name="lastname" type="xsd:string"/>
          <xsd:element name="firstname" type="xsd:string"/>
          <xsd:element name="inscription" type="xsd:string"/>
        </xsd:all>
      </xsd:complexType>
    </xsd:schema>
  </types>
  <wsdl:portType name="soapInterfacePort">
    <wsdl:operation name="auth">
      <wsdl:input message="tns:authIn"/>
      <wsdl:output message="tns:authOut"/>
    </wsdl:operation>
    <wsdl:operation name="verifyMember">
      <wsdl:input message="tns:verifyMemberIn"/>
      <wsdl:output message="tns:verifyMemberOut"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="soapInterfaceBinding" type="tns:soapInterfacePort">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="auth">
      <soap:operation soapAction="https://third-party-organization.com/app/out/soap.php#auth"/>
      <wsdl:input>
        <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="https://third-party-organization.com/app/out/soap.php"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="https://third-party-organization.com/app/out/soap.php"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="verifyMember">
      <soap:operation soapAction="https://third-party-organization.com/app/out/soap.php#verifierUnAdherent"/>
      <wsdl:input>
        <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="https://third-party-organization.com/app/out/soap.php"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="https://third-party-organization.com/app/out/soap.php"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:message name="authIn"/>
  <wsdl:message name="authOut">
    <wsdl:part name="return" type="tns:tpo_connexion"/>
  </wsdl:message>
  <wsdl:service name="soapInterfaceService">
    <wsdl:port name="soapInterfacePort" binding="tns:soapInterfaceBinding">
      <soap:address location="https://third-party-organization.com/app/out/soap.php"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

And here is the configuration file I am trying to set up to make it work.

plugin: soap
wsdlFile: third_party_organization.wsdl

resources:
  - path: /app/out/soap.php
    operation: "https://third-party-organization.com/app/out/soap.php#auth"
    requestBody:
      xPath: "/soap-env:Envelope/soap-env:Body/ns0:auth"
      xmlNamespaces:
        soap-env: "http://schemas.xmlsoap.org/soap/envelope/"
        ns0: "https://third-party-organization.com/app/out/soap.php"
    response:
      file: authResponse.xml
  - path: /app/out/soap.php
    operation: "https://third-party-organization.com/app/out/soap.php#verifierUnAdherent"
    requestBody:
      xPath: "/soap-env:Envelope/soap-env:Body/ns0:verifierUnAdherent"
      xmlNamespaces:
        soap-env: "http://schemas.xmlsoap.org/soap/envelope/"
        ns0: "https://third-party-organization.com/app/out/soap.php"
    response:
      file: verifyMemberResponse.xml

I am able to launch the server using the Docker image with this Imposter configuration (which is correctly loaded) but when my script perform a request to Imposter, I get the following error:

mock-soap    | 20:18:52 WARN  i.g.i.s.HandlerServiceImpl - File not found: POST http://mock-soap:8080/app/out/soap.php

I thought that Imposter was supposed to catch the SOAP request to this file soap.php according to the pattern I defined in the configuration file. I do not have access to the soap.php file on the remote server. Is my configuration wrong or is my WSDL not compliance with Imposter?

Thank in advance for the help.

outofcoffee commented 1 month ago

Hi @rdruilhe, thanks for raising this. Please could you share the full log output when Imposter starts?

Ideally, it would be good to set the IMPOSTER_LOG_LEVEL=trace environment variable, to get the most detailed logs.

rdruilhe commented 1 month ago

Hello,

Thanks for the answer. After digging a bit, I found that I made a mistake due to an initial error in Imposter. Indeed, my WSDL starts with the following header :

<?xml version="1.0"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
             xmlns:tns="https://third-party-organization.com/app/out/soap.php"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
             name="soapInterface"
             targetNamespace="https://third-party-organization.com/app/out/soap.php">

But when I launch Imposter with this WSDL file, I get the following error :

mock-soap          | java.lang.IllegalStateException: More than one WSDL namespace found on root element: [[Namespace: prefix "" is mapped to URI "http://schemas.xmlsoap.org/wsdl/"], [Namespace: prefix "wsdl" is mapped to URI "http://schemas.xmlsoap.org/wsdl/"]]
mock-soap          |    at io.gatehill.imposter.plugin.soap.parser.VersionAwareWsdlParser.<init>(VersionAwareWsdlParser.kt:79) ~[imposter-mock-soap-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.plugin.soap.SoapPluginImpl.parseWsdls(SoapPluginImpl.kt:122) ~[imposter-mock-soap-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.plugin.soap.SoapPluginImpl.configureRoutes(SoapPluginImpl.kt:113) ~[imposter-mock-soap-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.Imposter.configureRoutes(Imposter.kt:221) ~[imposter-engine-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.Imposter.access$configureRoutes(Imposter.kt:88) ~[imposter-engine-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.Imposter$start$1.invokeSuspend(Imposter.kt:135) ~[imposter-engine-4.0.0.jar:?]
mock-soap          |    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-1.9.10.jar:1.9.10-release-459]
mock-soap          |    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:?]
mock-soap          |    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:?]
mock-soap          |    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:?]
mock-soap          |    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:?]
mock-soap          |    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:?]
mock-soap exited with code 0

The third party server accept this double header without raising any error but not Imposter. Thus, to configure Imposter properly, I removed the xmlns="http://schemas.xmlsoap.org/wsdl/" line and added wsdl: in front of every tag, except <description> (I forgot to add it to this tag). It lead to the error I mention in my previous post :

mock-soap    | 20:18:52 WARN  i.g.i.s.HandlerServiceImpl - File not found: POST http://mock-soap:8080/app/out/soap.php

Thus, I found the error and fixed it and now I am stuck with this error:

Attaching to mock-soap
mock-soap          | 09:56:07 TRACE i.g.i.c.ImposterLauncher - Searching metadata for plugins
mock-soap          | 09:56:08 TRACE i.g.i.u.ClassLoaderUtil - No plugin files found in /opt/imposter/plugins
mock-soap          | 09:56:08 TRACE i.g.i.u.ClassLoaderUtil - Plugins will use default classloader
mock-soap          | 09:56:08 TRACE i.g.i.c.u.MetaUtil - Read 2 config resolvers from metadata: [class io.gatehill.imposter.config.resolver.S3ConfigResolver, class io.gatehill.imposter.config.resolver.LocalFileConfigResolver]
mock-soap          | 09:56:08 TRACE i.g.i.c.u.ConfigUtil - Configuration resolvers: [class io.gatehill.imposter.config.resolver.S3ConfigResolver, class io.gatehill.imposter.config.resolver.LocalFileConfigResolver]
mock-soap          | 09:56:08 TRACE i.g.i.u.FeatureUtil - Features: {metrics=true, stores=true}
mock-soap          | 09:56:08 TRACE i.g.i.EngineBuilder - Initialising mock engine
mock-soap          | 09:56:08 INFO  i.g.i.Imposter - Starting mock engine 4.0.0
mock-soap          | 09:56:08 TRACE i.g.i.c.u.ConfigUtil - Excluded from config file search: [.git, .idea, .svn, node_modules]
mock-soap          | 09:56:08 TRACE i.g.i.c.u.ConfigUtil - Configuration files discovered in /opt/imposter/config: [ConfigReference(file=/opt/imposter/config/mock-soap-config.yaml, configRoot=/opt/imposter/config)]
mock-soap          | 09:56:08 TRACE i.g.i.Imposter - Engine config: ImposterConfig(host=0.0.0.0, listenPort=8080, configDirs=[/opt/imposter/config], serverUrl=http://localhost:8080, isTlsEnabled=false, keystorePath=classpath:/keystore/ssl.jks, keystorePassword=password, plugins=[io.gatehill.imposter.plugin.internal.MetaInfPluginDetectorImpl], pluginArgs={}, serverFactory=io.gatehill.imposter.server.vertxweb.VertxWebServerFactoryImpl, pluginDiscoveryStrategy=null, pluginDiscoveryStrategyClass=io.gatehill.imposter.plugin.DynamicPluginDiscoveryStrategyImpl, useEmbeddedScriptEngine=false)
mock-soap          | 09:56:08 DEBUG i.g.i.c.u.ConfigUtil - Loading configuration file: /opt/imposter/config/mock-soap-config.yaml
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - Discovered [473 ms] annotated plugins on classpath: {config-detector=io.gatehill.imposter.plugin.detector.ConfigPluginDetectorImpl, fake-data=io.gatehill.imposter.plugin.fakedata.FakeDataPlugin, meta-detector=io.gatehill.imposter.plugin.internal.MetaInfPluginDetectorImpl, openapi=io.gatehill.imposter.plugin.openapi.OpenApiPluginImpl, rest=io.gatehill.imposter.plugin.rest.RestPluginImpl, sfdc=io.gatehill.imposter.plugin.sfdc.SfdcPluginImpl, soap=io.gatehill.imposter.plugin.soap.SoapPluginImpl, wiremock=io.gatehill.imposter.plugin.wiremock.WiremockPluginImpl, js-detector=io.gatehill.imposter.scripting.common.JsPluginDetectorImpl, js-graal-compat=io.gatehill.imposter.scripting.graalvm.service.GraalvmCompatScriptServiceImpl, js-graal=io.gatehill.imposter.scripting.graalvm.service.GraalvmScriptServiceImpl, store-detector=io.gatehill.imposter.store.StorePluginDetectorImpl, store-dynamodb=io.gatehill.imposter.store.dynamodb.DynamoDBStoreFactoryImpl, store-graphql=io.gatehill.imposter.store.graphql.GraphQLQueryPlugin, store-inmem=io.gatehill.imposter.store.inmem.InMemoryStoreFactoryImpl, store-redis=io.gatehill.imposter.store.redis.RedisStoreFactoryImpl}
mock-soap          | 09:56:09 TRACE i.g.i.c.u.ConfigUtil - Loaded 1 plugin configuration file(s) with 0 error(s): [ConfigReference(file=/opt/imposter/config/mock-soap-config.yaml, configRoot=/opt/imposter/config)]
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - Registered plugin: js-detector with class: io.gatehill.imposter.scripting.common.JsPluginDetectorImpl
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - 1 plugin(s) provided by: js-detector
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - Registered plugin: store-detector with class: io.gatehill.imposter.store.StorePluginDetectorImpl
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - 1 plugin(s) provided by: store-detector
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - Registered plugin: io.gatehill.imposter.plugin.internal.MetaInfPluginDetectorImpl with class: io.gatehill.imposter.plugin.internal.MetaInfPluginDetectorImpl
mock-soap          | 09:56:09 TRACE i.g.i.c.u.MetaUtil - Read 7 plugins from metadata: {openapi=PluginMetadata(name=openapi, class=io.gatehill.imposter.plugin.openapi.OpenApiPluginImpl, load=LAZY), store-graphql=PluginMetadata(name=store-graphql, class=io.gatehill.imposter.store.graphql.GraphQLQueryPlugin, load=EAGER), rest=PluginMetadata(name=rest, class=io.gatehill.imposter.plugin.rest.RestPluginImpl, load=LAZY), fake-data=PluginMetadata(name=fake-data, class=io.gatehill.imposter.plugin.fakedata.FakeDataPlugin, load=EAGER), wiremock=PluginMetadata(name=wiremock, class=io.gatehill.imposter.plugin.wiremock.WiremockPluginImpl, load=LAZY), soap=PluginMetadata(name=soap, class=io.gatehill.imposter.plugin.soap.SoapPluginImpl, load=LAZY), sfdc=PluginMetadata(name=sfdc, class=io.gatehill.imposter.plugin.sfdc.SfdcPluginImpl, load=LAZY)}
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - 3 plugin(s) provided by: meta-detector
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - Registered plugin: js-graal with class: io.gatehill.imposter.scripting.graalvm.service.GraalvmScriptServiceImpl
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - Registered plugin: store-inmem with class: io.gatehill.imposter.store.inmem.InMemoryStoreFactoryImpl
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - Registered plugin: config-detector with class: io.gatehill.imposter.plugin.detector.ConfigPluginDetectorImpl
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - 1 plugin(s) provided by: config-detector
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - Registered plugin: store-graphql with class: io.gatehill.imposter.store.graphql.GraphQLQueryPlugin
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - Registered plugin: fake-data with class: io.gatehill.imposter.plugin.fakedata.FakeDataPlugin
mock-soap          | 09:56:09 TRACE i.g.i.p.DynamicPluginDiscoveryStrategyImpl - Registered plugin: io.gatehill.imposter.plugin.soap.SoapPluginImpl with class: io.gatehill.imposter.plugin.soap.SoapPluginImpl
mock-soap          | 09:56:10 TRACE i.g.i.l.LifecycleHooks - Registered listener: io.gatehill.imposter.store.service.CaptureServiceImpl
mock-soap          | 09:56:10 TRACE i.g.i.l.LifecycleHooks - Registered listener: io.gatehill.imposter.service.script.ScriptedResponseServiceImpl
mock-soap          | 09:56:10 TRACE i.g.i.l.LifecycleHooks - Registered listener: io.gatehill.imposter.service.security.SecurityLifecycleListenerImpl
mock-soap          | 09:56:10 TRACE i.g.i.s.s.StoreServiceImpl - Stores enabled
mock-soap          | 09:56:10 TRACE i.g.i.l.LifecycleHooks - Registered listener: io.gatehill.imposter.store.service.StoreServiceImpl
mock-soap          | 09:56:10 TRACE i.g.i.l.LifecycleHooks - Registered listener: io.gatehill.imposter.store.service.StoreServiceImpl
mock-soap          | 09:56:10 TRACE i.g.i.l.LifecycleHooks - Registered listener: io.gatehill.imposter.store.graphql.GraphQLQueryService
mock-soap          | 09:56:10 TRACE i.g.i.l.LifecycleHooks - Registered listener: io.gatehill.imposter.store.service.StoreRestApiServiceImpl
mock-soap          | 09:56:10 TRACE i.g.i.p.PluginManager - Starting plugins with 1 configs
mock-soap          | 09:56:10 DEBUG i.g.i.p.PluginManager - Loaded 9 plugin(s): [js-detector, store-detector, meta-detector, js-graal, store-inmem, config-detector, store-graphql, fake-data, soap]
mock-soap          | 09:56:11 TRACE i.g.i.l.LifecycleHooks - Registered listener: io.gatehill.imposter.plugin.fakedata.FakeDataPlugin
mock-soap          | 09:56:11 TRACE i.g.i.Imposter - Metrics enabled
mock-soap          | 09:56:11 DEBUG i.g.i.p.s.p.VersionAwareWsdlParser - Using WSDL parser: V1 for: /opt/imposter/config/extranet_pro.wsdl
mock-soap          | 09:56:11 ERROR i.v.c.i.l.c.VertxIsolatedDeployer - Failed in deploying verticle
mock-soap          | java.lang.IllegalStateException: No input found for portType operation: auth
mock-soap          |    at io.gatehill.imposter.plugin.soap.parser.Wsdl1Parser.getInputOrOutput(Wsdl1Parser.kt:211) ~[imposter-mock-soap-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.plugin.soap.parser.Wsdl1Parser.getOperation(Wsdl1Parser.kt:188) ~[imposter-mock-soap-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.plugin.soap.parser.Wsdl1Parser.getBinding(Wsdl1Parser.kt:99) ~[imposter-mock-soap-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.plugin.soap.parser.VersionAwareWsdlParser.getBinding(VersionAwareWsdlParser.kt:94) ~[imposter-mock-soap-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.plugin.soap.SoapPluginImpl.parseWsdls(SoapPluginImpl.kt:129) ~[imposter-mock-soap-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.plugin.soap.SoapPluginImpl.configureRoutes(SoapPluginImpl.kt:113) ~[imposter-mock-soap-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.Imposter.configureRoutes(Imposter.kt:221) ~[imposter-engine-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.Imposter.access$configureRoutes(Imposter.kt:88) ~[imposter-engine-4.0.0.jar:?]
mock-soap          |    at io.gatehill.imposter.Imposter$start$1.invokeSuspend(Imposter.kt:135) ~[imposter-engine-4.0.0.jar:?]
mock-soap          |    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-1.9.10.jar:1.9.10-release-459]
mock-soap          |    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:?]
mock-soap          |    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:?]
mock-soap          |    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:?]
mock-soap          |    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:?]
mock-soap          |    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:?]
mock-soap exited with code 0

I think this is an error from the WSDL but because the WSDL is working with the third party server and not with Imposter, I am stuck not knowing what to do: I do not have the possibility to modify the third party server code (because it is a private server) and I am stuck with this WSDL not working with Imposter.

Is there a way to bypass the validation of the WSDL at start in Imposter ? Or should I use REST plugin instead so that there is no validation of the WSDL file ?

Thanks for the help.

outofcoffee commented 1 month ago

Hi @rdruilhe, thank you for the diagnosis. Looking at the messages in the WSDL:

  <wsdl:message name="authIn"/>
  <wsdl:message name="authOut">
    <wsdl:part name="return" type="tns:tpo_connexion"/>
  </wsdl:message>

…it looks like the input message authIn doesn’t have any parts. This is the message referenced by the auth operation.

Do you have an example of the SOAP request message you expect to send? We might be able to use this to compare against the WSDL message to see if this looks right.

outofcoffee commented 1 month ago

Doing some research on this, my read of the WSDL 1.1 specification implies that a <message> element is expected to contain at least one <part> element, and defining a <message> with no parts would not comply with the specification.

According to the specification:

"Messages consist of one or more logical parts. Each part is associated with a type from some type system using a message-typing attribute."

You can find this in Section 2.3 of the WSDL 1.1 specification: WSDL 1.1 Specification, Section 2.3.

This doesn’t explain why the real server you mentioned doesn’t complain, though 😅

rdruilhe commented 1 month ago

Hi @outofcoffee, thanks for the information. Indeed, it seems that the WSDL the third party organization provided to us is not compliant with the WSDL specification. I just added a <part> element to the WSDL and Imposter no longer complain and is able to launch correctly.

Do you have an example of the SOAP request message you expect to send? We might be able to use this to compare against the WSDL message to see if this looks right.

Yes, I just retrieved one and here it is:

<?xml version='1.0' encoding='utf-8'?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"
                   xmlns:ns0="https://third-party-organization.com/app/out/soap.php"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
                   soap-env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <soap-env:Body>
        <ns0:auth xmlns:ns0="https://third-party-organization.com/app/out/soap.php"/>
    </soap-env:Body>
</soap-env:Envelope>

It looks compliant with the WSDL I provided. And here is the response (also compliant):

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
                   xmlns:ns1="https://third-party-organization.com/app/out/soap.php"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
                   SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <ns1:authResponse>
            <return xsi:type="ns1:tpo_connexion">
                <user xsi:type="xsd:string"></user>
                <password xsi:type="xsd:string"></password>
            </return>
        </ns1:authResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

But despite this lack of compliance with the WSDL specification and the fact that the remote server seems to have no problem with that, what do you recommend for testing our code? Is Imposter the correct answer to test not compliant WSDL or should I develop a small SOAP server instead for my tests? Is the use of Imposter REST plugin is a solution, because this plugin will not take into account the content of the message and thus the compliance with the specification?