Set a Daemon token in modules context-sensitive tests

@dkayiwa @ibacher @mseaton @wyclif, how do you guys setup a Daemon token for context-sensitive tests?

I had to resort to reflection:

@Before
public void setup() throws Exception {
    Field field = ModuleFactory.class.getDeclaredField("daemonTokens");
    field.setAccessible(true);
    Map<String, DaemonToken> daemonTokens = (Map<String, DaemonToken>) field.get(ModuleFactory.class);
    daemonTokens.put("test", new DaemonToken("test"));
    field.setAccessible(false);
}

What’s the proper/recommended way?

1 Like

If your test is in the org.openmrs.module package, you can use ModuleFactory.passDaemonToken(module)

Thanks @dkayiwa. Yes I had seen that, however I wasn’t sure what to do with the Module instance. But all of a sudden I realise that I could bring in a mock/test one just for the sake of going through this method.

Would be good to have all that baked into BaseModuleContextSensitiveTest actually.

All you need is set the moduleId on a module instance and you are good.

I agree!

This worked like a charm:

package org.openmrs.module;

import java.util.stream.Stream;

public class TestDaemonToken {
    
    private class Activator extends BaseModuleActivator implements DaemonTokenAware {
        
        private DaemonToken token;
        
        @Override
        public void setDaemonToken(DaemonToken token) {
            this.token = token;
        }
        
        public DaemonToken getDaemonToken() {
            return token;
        }
        
    }
    
    /**
     * Sets a valid Daemon token to a collection of {@link DaemonTokenAware} instances so that they
     * can run Daemon threads in tests.
     * 
     * @param awares The collection of {@link DaemonTokenAware} implementations.
     */
    public void setDaemonToken(DaemonTokenAware... awares) {
        Module module = new Module("Spring Test Module");
        module.setModuleId("spring-test-module");
        final Activator activator = new Activator();
        module.setModuleActivator(activator);
        ModuleFactory.passDaemonToken(module);
        
        Stream.of(awares).forEach(a -> a.setDaemonToken(activator.getDaemonToken()));
    }
}

Right now it will enter the code base of OAuth 2 Login, but I’ll create a ticket for having this moved to Core and backported.

Thanks for sharing the solution! :slight_smile:

1 Like

:+1:

For the record

  • new TestDaemonToken().setDaemonToken(aware) is used here.
  • TestDaemonToken is declared here.

hello @mksd , @dkayiwa whats the proper way to Pass a daemon token to the given module , like where do i do it from .

I want to Execute the given runnable in a new thread that is authenticated as the daemon user but am still failing to pass a valid DaemonToken here .

I had innitaialy run this in a hacky way ,Opened and Closed ContextSession, and added ProxyPrivileges , but i want to instead excute it in a deamon thread .

trying something like this

    Daemon.runInDaemonThread(new Runnable() {
            @Override
            public void run() {
                measureService.saveMeasurementEventData(measurementEventData);
            }
        }, new DaemonToken("radiology"));

Oh i have got it , i only had to have my activator implement DaemonTokenAware
:slightly_smiling_face:

2 Likes

HI @mksd , i only have issues setting Daemon token in the tests .

Not sure how you were able to autowire DaemonTokenAware here , its faling on my end and i cant seem to find where that was defined as a bean . am depending on OpenMRS version 2.0.0 . did you do any custom Spring Config some where else ??

Here I believe: openmrs-module-oauth2login/UsernameAuthenticationScheme.java at 611fe5ff60365d912760dcf05a55188ba0c2529c · openmrs/openmrs-module-oauth2login · GitHub

UsernameAuthenticationScheme is the only DaemonTokenAware defined as a bean in the context of that test.

That will not work with an activator, unless you make it a bean just for Spring tests.

makes sense .

however i have just done a little refactoring

I defined static DaemonToken property from the class i want to acces the token from

public class MeasurementEventListener implements ApplicationListener<MeasurementEvent> {
    @Autowired
    MeasurementService measureService;
    
    private static DaemonToken daemonToken;
    public static void setDaemonToken(DaemonToken token) {
		daemonToken = token;
	}

    @Override
    public void onApplicationEvent(MeasurementEvent event) {
       

        Daemon.runInDaemonThread(new Runnable() {
            @Override
            public void run() {
                measureService.saveMeasurementEventData(measurementEventData);
            }
        }, daemonToken);
    }
}

i refactored the TestDaemonToken class and removed the
DaemonTokenAware... awares
parameter

package org.openmrs.module;

import org.openmrs.module.radiology.viewer.event.MeasurementEventListener;

    public class TestDaemonToken {
    	
    	private class Activator extends BaseModuleActivator implements DaemonTokenAware {
    	
    		@Override
    		public void setDaemonToken(DaemonToken token) {
    			MeasurementEventListener.setDaemonToken(token);
    		}
    		
    	}
    	
    	/**
    	 * setts  a valid Daemon Token
    	 */
    	public void setDaemonToken() {
    		Module module = new Module("Spring Test Module");
    		module.setModuleId("Radiology");
    		final Activator activator = new Activator();
    		module.setModuleActivator(activator);
    		ModuleFactory.passDaemonToken(module);
    	}
    }

and i use it in the tests as

    @Before
	public void init() throws Exception {
		
		new TestDaemonToken().setDaemonToken();
	}

and it works fine

1 Like

Difficult to say what exact issue you may have encountered without sharing the entire code.

The class that I shared was meant to be entirely generic, hopefully entering Core’s test tooling.

1 Like

@mozzy there is a different between units tests and deployed environments, daemon token is only passed via the module activator by implementing DaemonTokenAware which is not called for unit tests. For tests, I would say just mock things.

2 Likes